Repository navigation
chore(format): align oxfmt with sanity-io/ui (^0.71.0, config), pre-commit hook on lefthook - #2047
Conversation
Same version as sanity-io/ui. The lockfile only moves oxfmt, its platform bindings and its tinypool dependency, to the same versions ui's lockfile resolves. Supersedes #1919.
Ignore storybook-static/** and CHANGELOG.md like ui's .oxfmtrc.json. ui's **/sanity.types.ts stays out: it no longer matches anything in ui, while plugins formats dev/test-studio/sanity.types.ts on purpose. Point the Oxc VS Code extension at oxfmt.config.ts: .oxfmtrc.json was deleted in #1565, and with a missing oxc.fmt.configPath the oxfmt language server formats with oxfmt's defaults (double quotes, semicolons, unsorted imports) instead of the repo's settings.
The hook only ran oxfmt on staged files. sanity-io/ui has no pre-commit hook: formatting comes from `pnpm format` and the Auto format workflow on main, which this repo already runs. Existing clones need no cleanup: husky's leftover .husky/_ runner exits 0 once .husky/pre-commit is gone.
🦋 Changeset detectedLatest commit: 123af25 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
✅ E2E Tests🟢 43 passed • 🟡 1 flaky • view full report • view run Studio: https://plugins-e2e-test-studio-l9v661854.sanity.dev Datasets: |
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||
…he oxfmt alignment PR Dropping the `.changeset/*.md` `singleQuote: false` override from the `@sanity/plugin-kit/oxfmt` preset is formatter config, which the oxfmt alignment (#2047) owns, not the Changesets setup (config, versions, scripts and release flow). Restore the preset as it is on main, drop its `@sanity/plugin-kit` patch changeset, and format the remaining changesets with the preset's double quotes again.
… sanity-io/ui sanity-io/ui removed the same Prettier-era singleQuote: false override when it switched Changesets to oxfmt formatting (sanity-io/ui#2957). Changesets already formats the changesets it writes with oxfmt here: its default format: "auto" detects oxfmt.config.ts, and #2046 makes that explicit with "format": "oxfmt". Without the override, both write single quotes, the same as pnpm format.
Requotes this PR's pending changeset now that the preset no longer keeps .changeset/*.md files on double quotes. Rerun after rebasing onto any branch that adds double-quoted changesets.
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 (oxfmt 0.71.0 with tinypool 2.2.0, husky and lint-staged removed). pnpm also recomputes the peer suffixes of snapshots that have oxfmt as an optional peer (sanity@next, @sanity/cli); clean main plus this branch's package.json and catalog changes yields the same lockfile.
…2048) <!-- CURSOR_AGENT_PR_BODY_BEGIN --> Puts this repo on the same pnpm version and settings as [sanity-io/ui](https://github.com/sanity-io/ui) `main`: `packageManager` `pnpm@12.6.0`, ui's pnpm settings in `pnpm-workspace.yaml`, ui's CI setup action (`pnpm/setup@v3`) but with its store cache off, and a lockfile regenerated with pnpm 12.6.0. The `Dedupe lockfile` workflow stays, with the one change pnpm 12 needs. ## Builds on #2044, supersedes #2043, #2044 and #1131 - **#2044** (pnpm 12.6.0 + `autoDedupe` + Vercel CLI bump) is the base. Its commits are cherry-picked with `-x`. #2044's interim `installCommand` workaround in `dev/e2e-studio/vercel.json`, which it later removed, is left out, so this PR doesn't touch any Vercel config. #2044 left CI on `pnpm/action-setup` + `actions/setup-node`; this PR switches to ui's action. - **#2043** (Renovate's plain `packageManager` bump) fails `E2E / deploy-preview`, because `vercel@57` can't parse pnpm 12's lockfile (see below). - **#1131** (an early `pnpm/setup@v1` attempt) is covered by the CI change here. - **#2018** (lockfile regenerated from scratch) is not superseded: it re-resolves every dependency, which is out of this PR's scope. ## Changes | | Before | After | | --- | --- | --- | | `packageManager` | `pnpm@11.21.0` | `pnpm@12.6.0` (as in ui) | | `pnpm-workspace.yaml` | | `autoDedupe: true` and `strictPeerDependencies: false` (as in ui) | | `.github/actions/setup` | `pnpm/action-setup@v6` + `actions/setup-node@v7` + `pnpm install` | ui's action (`pnpm/setup@v3`, `runtime: node@lts`) with `cache: false`, where ui has `cache: true` | | `.github/workflows/pnpm-if-needed.yml` (`Dedupe lockfile`) | runs `pnpm dedupe` once | kept; runs `pnpm dedupe` twice | | `vercel` (root devDependency) | `^57.0.0` | `^60.1.3` | - **Lockfile:** main's lockfile after #2039, plus this PR's changes as pnpm 12.6.0 re-applied them. Those changes are the `packageManagerDependencies` block, the `vercel@60.1.3` tree with the optional-peer suffixes it brings, and `autoDedupe`'s dedupes: the same `zod` and `gaxios` ones as the pending #2052, plus the peer toggle described below. No other versions change, and `sanity@6.17.0-next.108` stays put. #2039's refresh had already removed the three duplicates the first revision collapsed. - **Vercel CLI:** pnpm 12 writes the lockfile as two YAML documents. `vercel@57`'s bundled `@vercel/build-utils` reads only the first, falls back to npm, and fails on `workspace:*`, which is #2043's failure. `58.9.5` is the first CLI that parses it, and every release from `58.7.0` on ships without npm provenance (still published from GitHub Actions OIDC). So `trustPolicy: no-downgrade` needs the exact versions pnpm reports as `ERR_PNPM_TRUST_DOWNGRADE` in `trustPolicyExclude`: `vercel@60.1.3` and 31 `@vercel/*` packages. - **CI setup:** `pnpm/setup` reads `packageManager`, installs Node LTS, runs `pnpm install`, and caches pnpm's lockfile-verification verdict across jobs, which saves the ~28s supply-chain check. `ailf-eval.yml` passes `runtime: node@lts` instead of `node-version: lts/*`. `release.yml` keeps `pnpm/action-setup` + `actions/setup-node`, as in ui. - **Store cache off, unlike ui** (commit `bddef7a4`). With `cache: true`, `pnpm/setup` uploads a new ~645 MiB store entry after every job, even for an unchanged lockfile, because its keys include the run id. That was 12 entries (7.7 GB) per push here, evicting the Playwright browser caches the e2e jobs reuse. With it off, test shards spend 16–19s in setup and nothing afterwards, against 19–23s plus 9–12s of upload with it on. ui keeps `cache: true` and has the same issue. Revert `bddef7a4` to match ui. - **`Dedupe lockfile` workflow kept, adapted for pnpm 12** (commit `a7e1057f`). On the current lockfile, pnpm 12.6.0's `pnpm dedupe` isn't idempotent. Each run toggles `@vitejs/devtools` and `oxc-transform-react` in the peer suffixes of `plugins/@sanity/debug-preview-url-secret-plugin`'s `sanity` and `@sanity/tsdown-config` entries, without changing any version. Run once, as before, the workflow would open a toggle-only PR after every lockfile change on `main`, and each automerged dedupe PR would trigger the next. A second run undoes the toggle and keeps real dedupes. An earlier commit in this branch deleted the workflow; `82810b5e` reverts that. - **Docs:** AGENTS.md, CONTRIBUTING.md and README.md now require pnpm v12. AGENTS.md and `e2e/README.md` explain the two-document lockfile and the Vercel CLI floor; per Copilot's review of #2044, the trust-policy wording only lists versions that pnpm reports as downgrades. AGENTS.md also says not to commit lockfile diffs that only flip the peer toggle. No changeset: no published package's manifest or runtime code changes, as with the pnpm 11 bump in #1579. ## Merge with main `main` gained one commit after this branch started: Renovate's lock file maintenance (#2039, `cb68d65e`). It conflicted only in `pnpm-lock.yaml`. `main` is merged in rather than rebased, so nothing was force-pushed. The resolution takes main's lockfile and lets pnpm 12.6.0 re-apply this PR's changes; `pnpm-workspace.yaml` needed no further changes. #2039's 25 source files merged cleanly. ## What Cody needs to do - **Add the "🤖 bot" label.** The agent's label tool can't edit PRs in this repo. - **Close #2044 and #1131** when this lands. Renovate autocloses #2043. The pending dedupe PR #2052 is redundant, since its four lines are already in this lockfile. It merges cleanly with this PR in either order. - **Vercel:** nothing to set up. No project, env var or `vercel.json` changes; only the root `vercel` CLI devDependency moves. - **Secrets and ruleset:** no new secrets, and the required checks (`lint`, `build`, `test`, Socket) keep their names. ## Open questions 1. **Vercel CLI trust exclusions.** This is #2044's call, which overrides the reason #1890 was closed: 32 exact versions without provenance in `trustPolicyExclude`. Every future `vercel` bump has to add its own versions, so Renovate's `vercel` PRs will fail their artifact update until someone does. The alternative is e2e previews that can't build on pnpm 12. 2. **`strictPeerDependencies: false`** matches ui but is already pnpm's default; ui carried it over from an old `.npmrc`. Keep it for parity, or drop it? 3. **Store cache in ui.** ui has the same per-job uploads. Turn its store cache off too? ## Known pnpm 12.6.0 issue The peer toggle also shows up locally. A fresh `pnpm install`, or the implicit install pnpm runs before scripts when `node_modules` looks stale, flips those two suffixes in `pnpm-lock.yaml`. Frozen installs, which CI uses, never do. The lockfile before #2039 didn't toggle; main's refreshed resolutions exposed it, and it happens with `autoDedupe` off too. ## Not in this PR - vitest 5, oxfmt (#2047), oxlint (#2049) and the tsdown setup are sibling PRs. Whichever lands later merges `main` and regenerates the lockfile. - ui's `packageExtensions` entry for `@testing-library/jest-dom` belongs to its vitest 5 setup. - ui's root `engines.node: ">=22.13"` and macOS test job are Node and test-matrix changes, not pnpm. Plugins' tooling needs Node ≥ 22.18 anyway (AGENTS.md). - Plugins-specific pnpm settings that ui doesn't have (`trustPolicy`, `dedupePeers`, `dedupeDirectDeps`, `publicHoistPattern`) stay. ## Verification - **Lockfile:** regenerated on `main` at `cb68d65e` by pnpm 12.6.0 run directly on Node 24.21.0. A cold `pnpm install --frozen-lockfile` passes the supply-chain check (2,615 entries), and without the Vercel exclusions it fails with `ERR_PNPM_TRUST_DOWNGRADE`. - **Dedupe workflow:** from the committed lockfile, from its toggled state, and from the committed lockfile with #2052's duplicates put back, two `CI=true pnpm dedupe --config.ignore-scripts=true` runs give, respectively, no change, no change, and exactly #2052's dedupes. A single run gives the toggle every time. - **Local checks on the merged tree:** `pnpm lint`, `pnpm knip`, `pnpm build` (52/52 tasks) and `pnpm test run` (224 files, 1,391 tests) pass. - **CI:** everything passes on `bddef7a4`, including `deploy-preview` (Vercel CLI 60.1.3 detects the lockfile and installs with pnpm 12.6.0) and e2e on Chromium and Firefox. CI on `a7e1057f`, which only touches the dedupe workflow and AGENTS.md, is running. - **zizmor:** `zizmor .github` reports the same findings as on `main` at every severity. <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-0c45b56b-fd33-55f7-b22c-89bde73748e4?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> <a href="https://cursor.com/background-agent?bcId=bc-0c45b56b-fd33-55f7-b22c-89bde73748e4&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> </div> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Brings in pnpm 12.6.0 from #2048. package.json keeps both sides: main's packageManager pnpm@12.6.0 and vercel ^60.1.3, without this branch's removed lint-staged block. pnpm-lock.yaml is main's lockfile regenerated with pnpm 12.6.0. Versions only move for this branch's own changes (oxfmt 0.71.0 with tinypool 2.2.0; husky, lint-staged and their exclusive dependencies removed). pnpm also re-derives the peer contexts of snapshots that have oxfmt as an optional peer, which adds sanity@6.17.0-next.108 and @sanity/cli@8.13.0 variants. Clean main plus this branch's package.json and catalog changes yields the identical lockfile. pnpm format changes nothing.
The merge's regenerated lockfile carried the peer toggle that AGENTS.md describes: a non-frozen install flips @vitejs/devtools and oxc-transform-react in the peer suffixes of @sanity/debug-preview-url-secret-plugin's sanity and @sanity/tsdown-config entries, adding sanity and @sanity/cli snapshot variants, without changing any version. This restores main's side of the toggle, so pnpm-lock.yaml differs from main only by this branch's own changes: oxfmt 0.71.0 with tinypool 2.2.0, and husky, lint-staged and their exclusive dependencies removed.
… 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> <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> </div> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Brings in the themer move from #2046. Only pnpm-lock.yaml conflicted. pnpm-lock.yaml is main's lockfile regenerated with pnpm 12.6.0, minus the peer toggle AGENTS.md describes (debug-preview-url-secret-plugin's sanity and @sanity/tsdown-config suffixes). It differs from main only by this branch's own changes: oxfmt 0.71.0 with tinypool 2.2.0, and husky, lint-staged and their exclusive dependencies removed. sanity@6.17.0-next.108 doesn't move.
Requotes the three changesets from #2046 (dashboard-widget-vercel-xstate, shared-deps-sanity-ui-icons-motion and themer-plugins-monorepo) now that the oxfmt preset no longer keeps .changeset/*.md files on double quotes. Quote-only changes; the themer package and dev/storybook already pass.
Brings in the Version Packages release (#2053), which consumed the three changesets from #2046 that this branch had just requoted. The modify/delete conflicts resolve to main's deletions. #2053 didn't change pnpm-lock.yaml, so this branch's lockfile (main's, regenerated with pnpm 12.6.0, minus the peer toggle) still applies. pnpm format changes nothing.
Brings back a pre-commit hook, set up the way sanity-io/sanity replaced husky and lint-staged: lefthook ^2.1.14 as a root devDependency, its install script allowed in pnpm-workspace.yaml (it installs the hook and skips when CI is set), and a lefthook.yml whose parallel jobs run oxfmt on staged scripts and on other files with stage_fixed. sanity's oxlint command is left to the oxlint alignment. The jobs call node_modules/.bin/oxfmt instead of sanity's pnpm oxfmt, because running pnpm in a hook triggers pnpm 12's implicit install, which flips the lockfile's peer toggle. pnpm-lock.yaml only gains lefthook 2.1.14 and its platform packages. It was resolved through a local registry proxy that served the locked 6.17.0-next.108 as the next dist-tag, as AGENTS.md describes, so the Studio overrides stay put.
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The migration is internally consistent, documented, comprehensively verified, and has no unresolved code issues.
Review effort: Balanced
Findings: None
What changed in this PR
Aligns repository formatting with sanity-io/ui and migrates the pre-commit formatter from Husky/lint-staged to Lefthook.
Changes:
- Upgrades oxfmt to 0.71 and aligns configuration.
- Adds Lefthook-based staged-file formatting.
- Updates plugin-kit changesets and contributor documentation.
| File | Description |
|---|---|
.changeset/plugin-kit-oxfmt-0-71.md |
Records the oxfmt peer update. |
.changeset/plugin-kit-oxfmt-changeset-quotes.md |
Records the preset quoting change. |
.husky/pre-commit |
Removes the obsolete Husky hook. |
.vscode/settings.json |
Points VS Code to the active formatter config. |
AGENTS.md |
Documents formatting and Lefthook workflows. |
lefthook.yml |
Adds staged-file formatting jobs. |
oxfmt.config.ts |
Aligns workspace ignore patterns. |
package.json |
Replaces Husky/lint-staged with Lefthook. |
packages/@sanity/plugin-kit/src/oxfmt.ts |
Removes the changeset quote override. |
pnpm-lock.yaml |
Locks updated formatter and hook dependencies. |
pnpm-workspace.yaml |
Updates the catalog and permits Lefthook installation. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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.
Brings in the oxfmt 0.71 alignment with the lefthook pre-commit hook (#2047) and the Version Packages release (#2053). - pnpm-workspace.yaml: kept both sides of the adjacent catalog lines (main's oxfmt ^0.71.0, this branch's oxlint ^1.86.0 and oxlint-tsgolint ^7.0.2003). - pnpm-lock.yaml: took main's lockfile and let pnpm 12.6.0 re-apply this branch's catalog change, so it differs from main only by the oxlint and oxlint-tsgolint catalog specifiers. The `next` dist-tag has moved to 6.17.0-next.112 since main's lockfile was written, so a plain re-resolution would have upgraded the Studio; as AGENTS.md describes, it ran through a local registry proxy serving the locked 6.17.0-next.108 as `next`, and a second re-resolution undid the known peer toggle on plugins/@sanity/debug-preview-url-secret-plugin.
#2047 dropped the plugin-kit oxfmt preset's .changeset/*.md override, so changeset frontmatter now uses single quotes like every other file.
Brings in oxfmt 0.71 and the lefthook pre-commit hook from #2047, and the Version Packages release from #2053. pnpm-lock.yaml is main's, regenerated with pnpm 12.6.0. npm's `next` dist-tag for the Studio packages moved to 6.17.0-next.112 this morning, so the regeneration resolved through a local registry proxy that serves 6.17.0-next.108 as `next`, as AGENTS.md describes; the lockfile has no proxy or tarball entries. Versions only move for this branch's toolchain, the rolldown-plugin-dts patch applies with the same hash, and `pnpm install --fix-lockfile` reverted the debug-preview-url-secret-plugin optional-peer toggle. The result is identical to git's textual merge of the two lockfiles.
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @sanity/themer@0.9.0 ### Minor Changes - [#2065](#2065) [`e1c357b`](e1c357b) Thanks [@stipsan](https://github.com/stipsan)! - Adopt <Activity>, <ViewTransition> and React.lazy APIs according to best practice. Removes the top-level default theme config, it will be added back again later ## @sanity/plugin-kit@10.0.11 ### Patch Changes - [#2047](#2047) [`89bbaf7`](89bbaf7) Thanks [@stipsan](https://github.com/stipsan)! - Update the optional `oxfmt` peer dependency to ^0.71.0 - [#2047](#2047) [`89bbaf7`](89bbaf7) Thanks [@stipsan](https://github.com/stipsan)! - The `@sanity/plugin-kit/oxfmt` preset no longer formats `.changeset/*.md` files with double quotes, so their frontmatter gets single quotes like the rest of your code. Changesets 3 formats the changesets it writes with oxfmt when it finds your `oxfmt.config.ts`, or when `.changeset/config.json` sets `"format": "oxfmt"`, so they come out the same way. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Themer’s minor release documents a breaking-adjacent removal of default theme config; plugin-kit is tooling-only but may change how changeset files format in repos using the oxfmt preset. > > **Overview** > This is a **Changesets release** PR that publishes two npm packages by bumping versions, folding pending changesets into each package’s `CHANGELOG.md`, and deleting the consumed `.changeset/*.md` files. > > **`@sanity/plugin-kit@10.0.11`** (patch): release notes record a bump of the optional `oxfmt` peer to `^0.71.0` and a tweak to the `@sanity/plugin-kit/oxfmt` preset so `.changeset/*.md` frontmatter uses single quotes, aligning with Changesets 3 when oxfmt is enabled. > > **`@sanity/themer@0.9.0`** (minor): release notes cover adopting React `<Activity>`, `<ViewTransition>`, and `React.lazy` patterns, and **removing the top-level default theme config** (called out as temporary until it returns). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 7b2b0b2. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
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
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 andtinypool2.2.0, the same versions and integrity hashes as ui's lockfile. There's a patch changeset for@sanity/plugin-kit, whose optionaloxfmtpeer uses the catalog.chore(format): align the oxfmt config with sanity-io/ui: rootoxfmt.config.tsnow ignoresstorybook-static/**andCHANGELOG.md, like ui's.oxfmtrc.json..vscode/settings.jsonpointsoxc.fmt.configPathatoxfmt.config.tsinstead of the.oxfmtrc.jsonthat Migrate plugin-kit and the monorepo from prettier/eslint to oxfmt/oxlint #1565 deleted. AGENTS.md records the parity with ui and the exceptions.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.chore(format): drop the oxfmt preset's .changeset/*.md override, like sanity-io/ui: the@sanity/plugin-kit/oxfmtpreset loses its Prettier-erasingleQuote: falseoverride for.changeset/*.md, as ui's.oxfmtrc.jsondid when ui switched Changesets to oxfmt formatting (chore: use changesets format oxfmt ui#2957). It gets a patch changeset for@sanity/plugin-kit, since the preset is published. This part moved here from feat(themer): move @sanity/themer from sanity-io/ui, with a Storybook and ui's Changesets setup #2046 because it's formatter config. AGENTS.md now explains how changesets get quoted.chore(format): run pnpm format: requotes this PR's own changeset from commit 1. Everything else already passed under oxfmt 0.71.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: trueinpnpm-workspace.yaml, as in sanity. Lefthook's own install script runslefthook installduringpnpm install, and it skips whenCIis set, which covers GitHub Actions and Vercel. There's no rootpostinstallscript: sanity dropped itslefthook install --reset-hooks-pathpostinstall in #12767 for this package build.lefthook.ymlwith sanity's layout: a parallelpre-commitwith two jobs that run oxfmt on staged files withstage_fixed: true. The jobs areformat-scripts(*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}) andformat-other(*.{md,yml,yaml,css,json,jsonc}).mts,ctsandjsoncare additions to sanity's globs. They cover this repo's.mts,turbo.jsoncandknip.jsoncfiles, which the old lint-staged glob partly covered.oxlint --fixline is left to the oxlint alignment (chore(lint): update oxlint to ^1.86.0 and align its config with sanity-io/ui #2049).node_modules/.bin/oxfmtinstead of sanity'spnpm oxfmt. Running pnpm in a hook triggers pnpm 12's implicit install, which flips the lockfile's peer toggle (see AGENTS.md). sanity switched topnpm oxfmtin #12953 because lefthook doesn't putnode_modules/.binonPATH; the path solves that too.pnpm-lock.yamlgains only lefthook 2.1.14 and its platform packages (100 lines). A plain install would also have bumped the Studio'snextoverrides from6.17.0-next.108to6.17.0-next.112, so I resolved it through a local registry proxy that served the locked versions asnext, as AGENTS.md describes. The lockfile has nolocalhostortarball:entries.LEFTHOOK=0, the one-time reset for clones that ran husky, andpnpm --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
mainmainis merged in, not rebased, so nothing was force-pushed. After each merge,pnpm-lock.yamlismain's lockfile regenerated with pnpm 12.6.0, invoked directly sopackageManagerDependenciesstays, minus the peer toggle that AGENTS.md describes.package.jsonconflicted next to the removedlint-stagedblock. The resolution keeps both sides:main'spackageManager: pnpm@12.6.0andvercel^60.1.3, without the block. A follow-up commit restoresmain's side of the peer toggle, which the regeneration had flipped.pnpm formatthen requoted feat(themer): move @sanity/themer from sanity-io/ui, with a Storybook and ui's Changesets setup #2046's three changesets in a separate commit:dashboard-widget-vercel-xstate,shared-deps-sanity-ui-icons-motionandthemer-plugins-monorepo, 45 frontmatter lines, quote-only. The newplugins/@sanity/themerpackage anddev/storybookalready passed.main's deletions. It didn't change the lockfile, andpnpm formatchanges nothing after the merge.pnpm-lock.yamldiffers frommainonly by this PR's own changes:tinypoolfrom 2.1.0 to 2.2.0.No other version moves, and the Studio stays on
6.17.0-next.108.How it compares with ui
printWidth,semi,singleQuote,bracketSpacing,quoteProps,sortImports,sortPackageJson). They come from the@sanity/plugin-kit/oxfmtpreset.dist/**,storybook-static/**,pnpm-lock.yamlandCHANGELOG.mdin both. Two differences remain:turbo/**/*.hbsis plugins-only (the turbo generator templates).**/sanity.types.tsis left out. In ui it was forapps/docs/sanity.types.tsand has matched nothing since the docs app went static. Here,pnpm typegenformatsdev/test-studio/sanity.types.tson purpose: the test studio runs typegen withformatGeneratedCode: falseand thenpnpm format. Ignoring the file would make the next TypeGen PR unformat it.oxfmt.config.tsre-exporting the published preset instead of ui's.oxfmtrc.json. Migrate plugin-kit and the monorepo from prettier/eslint to oxfmt/oxlint #1565 made that switch so the preset is exactly what the monorepo formats with, and plugin-kit'sverify-packageexpects plugins to use it. oxfmt, the Oxc extension and Changesets' formatter detection all readoxfmt.config.ts. See the open question.formatscript is identical to ui's (oxfmt . --no-error-on-unmatched-pattern). Neither repo checks formatting on PRs, and both run the same sharedformat-if-needed.ymlonmain, 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.oxc.fmt.configPathat its config file, but plugins pointed it at a file that doesn't exist. I droveoxfmt --lspdirectly: with the old setting, the editor silently formats with oxfmt's defaults (double quotes, semicolons, bracket spacing, unsorted imports), and withoxfmt.config.tsit matchespnpm format.Changesets
.changeset/config.jsonsets"format": "oxfmt"(from feat(themer): move @sanity/themer from sanity-io/ui, with a Storybook and ui's Changesets setup #2046). I called@changesets/write(whatchangeset adduses) directly. With the override it writes"@sanity/plugin-kit": patch; without it,'@sanity/plugin-kit': patch, the same aspnpm format.changeset versionis unaffected. It only handsCHANGELOG.mdfiles to the formatter. This PR ignores those, and oxfmt skips them and exits 0 thanks to--no-error-on-unmatched-pattern.changeset statusparses the single-quoted frontmatter. It lists this PR's two changesets for@sanity/plugin-kit10.0.11.What to expect after merging
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) achore(format): 🤖 ✨PR that requotes the changeset. ui has had one after most Renovate merges since chore: use changesets format oxfmt ui#2957: chore(format): 🤖 ✨ ui#2971, chore(format): 🤖 ✨ ui#2980, chore(format): 🤖 ✨ ui#2986, chore(format): 🤖 ✨ ui#2998, chore(format): 🤖 ✨ ui#3002, chore(format): 🤖 ✨ ui#3013 and chore(format): 🤖 ✨ ui#3024.core.hooksPath=.husky/_. There,lefthook installrefuses to install and prints how to fix it, andpnpm installcontinues. Until the reset, husky's leftover runner does nothing, so commits simply aren't formatted.git config --unset core.hooksPath && pnpm installfixes it.Related PRs
main, plus the config alignment. Close it if Renovate doesn't once this merges.pnpm-workspace.yaml, where itsoxlintcatalog lines sit next tooxfmt. Keep both sides. It could also add sanity'soxlint --fixline to theformat-scriptsjob, callingnode_modules/.bin/oxlintdirectly.pnpm-lock.yaml.mainand regenerates the lockfile without committing the peer toggle.What Cody needs to do
🤖 botlabel: the agent's label tool couldn't edit PRs in this repository.lefthook@2.1.14's install script (node postinstall.js) as a low-priority warning. That script is what installs the hook:allowBuilds: lefthook: trueenables it, as in sanity-io/sanity, and it skips whenCIis set. Mark it as acceptable risk if you agree. Socket's required checks pass either way.git config --unset core.hooksPath && pnpm installonce, so lefthook replaces husky's hook.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 keptoxfmt.config.ts, since the formatting result is already the same as ui's.Verification
pnpm-lock.yamluntouched each time:.vscode/settings.json(its folder is gitignored) is re-staged without the[FAILED]that lint-staged printed.CHANGELOG.md, which oxfmt ignores, passes as-is.core.hooksPath=.husky/_,lefthook installexits 1 with instructions and installs nothing, as described above.CI=true(lefthook's install script skips):pnpm install --frozen-lockfile, which leavespnpm-lock.yamluntouchedoxfmt --check(2,018 files)oxlintandknipvitest run(235 files, 1,509 tests)pnpm test:browser(4 files, 7 stories)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 withmain.