Imported from jameskerr/react-arborist (
AGENTS.md). Install upstream withnpx skills add jameskerr/react-arborist. Copyright stays with the author.
AGENTS.md
Orientation for AI coding agents working in this repo.
What this is
react-arborist is a React tree-view component: virtualized, drag-and-drop reorderable, multi-selectable, filterable. Published to npm as react-arborist. Canonical repo: https://github.com/jameskerr/react-arborist.
The library wraps react-window for virtualization, react-dnd (HTML5 backend by default) for DnD, and a small redux store for internal state.
Repo layout
Yarn 4 workspaces monorepo. Top-level scripts in the root package.json delegate into modules.
modules/react-arborist/— the published library. Source insrc/, built todist/main(CJS) anddist/module(ESM) viatsc. Unit tests are Jest.modules/showcase/— Next.js demo site used for manual testing and the public demo. Depends onreact-arboristasworkspace:*, but Next bundles its builtdist/, not itssrc/— see the caveat below.modules/e2e/— Cypress end-to-end tests that drive the showcase's static export.yarn workspace e2e testbootsserveagainstmodules/showcase/outand runs all specs.modules/docs/— the documentation site (Hugo + Tailwind).
Other notable files:
bin/release.mjs— release orchestration script, driven byyarn release. Bumps the version and pushes a tag; the tag push is what kicks off publishing.bin/publish— the actual npm publish step. Builds the library, copiesREADME.mdinto the library workspace, thennpm publishes from there. Invoked from CI by.github/workflows/publish.ymlon tag push; also runnable by hand.CHANGELOG.md— assembled release notes, one# Version X.Y.Zsection per release.bin/release.mjsgenerates each section from the pending.changes/entries at release time. To record a change you add a.changes/entry, not aCHANGELOG.mdedit (see "Adding a changeset" below)..changes/— one Markdown file per user-facing change (the "changeset"). Each PR adds its own file, so entries never conflict and PRs merge in any order.bin/release.mjsconsumes them at release time. The format is in.changes/README.md; the field-level gotchas are under "Adding a changeset" below.
Tooling
- Node: pinned by
.node-versionat the repo root (currently24.12.0); usefnm(or any tool that reads.node-version) to match locally — that's enough for everyday build/test. CI's publish workflow (.github/workflows/publish.yml) resolves Node via the range^24.15.0instead, because it installsnpm@12for OIDC Trusted Publishing and that npm needs Node>=24.15.0; the extra floor only matters when running the publish steps, not for local dev. (The Node version that runsnpm publishdoesn't affect what Node versions the package loads on — that's set by the build target.) - Package manager: Yarn 4.0.2 (
packageManagerfield in rootpackage.json). - Lint:
oxlint(yarn lint,yarn lint:fix). - Format:
oxfmt(yarn fmt,yarn fmt:check). - Unit tests: Jest, scoped to the library workspace (
yarn workspace react-arborist test). - E2E: Cypress (
yarn workspace e2e test).
Build caveat (read this before debugging showcase changes)
The showcase imports react-arborist from dist/, not src/. If you change library source and rebuild only the showcase, the change does not propagate — Next is still bundling the old dist/. Sequence:
yarn workspace react-arborist build # rebuild library dist
yarn workspace showcase build # then rebuild showcase
Or run yarn start from the root, which clean-builds the library then runs the library in watch + the showcase dev server in parallel.
The same caveat applies to e2e tests: Cypress drives the showcase's static export, so library changes need a library rebuild first.
Testing
Unit tests live alongside the source in modules/react-arborist/src/**/*.test.ts(x) and run on Jest + Testing Library (yarn workspace react-arborist test, or yarn test from inside the library workspace).
A passing run is not enough — read the console output and treat warnings as failures to fix, not noise to scroll past. Jest reports passing tests even when React or the libraries log to console.error/console.warn, so it's easy to let warnings accumulate. The common offender here is React's "An update to X inside a test was not wrapped in act(...)": some tree interactions (selection, focus) kick off an async scrollTo, whose state update resolves on a microtask after the synchronous act() scope from fireEvent/render has already closed. Wrap the interaction (or a trailing flush) in await act(async () => { … }) so that update lands inside an act scope. When you add or change a test, run the whole suite and confirm it is warning-clean before pushing.
Release process
Releases are driven by bin/release.mjs (yarn release). The script does git checks, runs tests, builds, bumps modules/react-arborist/package.json, commits, tags, pushes, and creates a GitHub Release. The tag push triggers .github/workflows/publish.yml, which npm publishes via OIDC Trusted Publishing — no npm token is involved.
Steps
- On
main, working tree clean, in sync with the remote. - Confirm the pending changes have
.changes/entries (they normally land with their PRs). You do not hand-editCHANGELOG.md— the script writes it. - Run
yarn release(no version argument needed — the bump is inferred). The script:- Verifies branch is
main, working tree is clean, local matches remote. - Runs
yarn workspace react-arborist testandyarn build-lib. - Reads
.changes/*.md— fails if there are none. Infers the bump from the entry types (breaking→ major,feature→ minor,fix→ patch; takes the largest). Pass an explicitpatch|minor|major|X.Y.Zonly to override. - Assembles a new
# Version X.Y.Zsection and prepends it toCHANGELOG.md, thengit rms the consumed.changes/files. - Bumps
modules/react-arborist/package.json, commits asvX.Y.Z(changelog + deletions + version in one commit), tagsvX.Y.Z. - Pushes the commit and tag to the tracking remote.
- Creates a GitHub Release using the assembled section as the body.
- Verifies branch is
gh run watchto watch the publish workflow. Confirm the new version on https://www.npmjs.com/package/react-arborist.
Flags
--preview— dry-run. Reads git state and builds, but does not commit, tag, push, or release.--any-branch— skip themaincheck and the remote sync check.--no-tests— skip the unit test step.--yes/-y— skip the interactive confirmation.
Agent guidance
Agents should not run yarn release themselves — it pushes tags, mutates npm, and creates a public GitHub Release. The maintainer cuts releases. An agent's job around a change is typically:
- Add a
.changes/entry for the change (details below) — not editCHANGELOG.mddirectly. A PR that touchesmodules/react-arborist/src/without one fails theChangesetCI check; apply theskip-changeloglabel for changes with no user-facing effect (refactors, tests, CI, docs). - Confirm
mainhas all the PRs (and their changesets) that should be in the release. - Optionally run
yarn release --preview(add--any-branch --no-testsoffmain) to verify the inferred bump and the assembled notes look right.
Adding a changeset
Create .changes/<short-slug>.md (slug is free-form; name it after the change, e.g. 313-drop-bottom-of-list.md). .changes/README.md has the full format; the parts that trip agents up:
type(required): one ofbreaking,feature,fix. This both files the entry under the matchingCHANGELOG.mdheading and sets the release bump (breaking→ major,feature→ minor,fix→ patch; the release takes the largest across all pending entries).pr(optional, normally omitted): the trailing(#NNN). Don't set it — you can't know a PR's number until the PR exists, and you don't need to. At release timebin/release.mjsfinds the commit that added the changeset file and asks GitHub (gh api .../commits/{sha}/pulls) which PR it belongs to, filling the number in automatically. Setpr:only to override the lookup, e.g. for a changeset committed straight tomainwith no PR to find (the release fails with a clear message telling you to add it in that case).credit(optional): the number of an earlier PR this one supersedes, rendered as(#NNN, originally #MMM). The repo has a long tail of stale PRs; usecreditto attribute the original author when you carry someone else's PR across the line. It is not for the issue you're fixing — an issue number here renders as if it were a superseded PR, which is wrong. Reference the fixed issue in the body text instead.- Body (everything after the closing
---): the changelog bullet text. Mention the issue being fixed here (e.g. "... again (issue #313).").
To sanity-check an entry before it ships, commit it first, then run yarn release --preview --any-branch --no-tests and read the assembled section it prints. The preview runs a working-tree-clean check before it parses .changes/, so it fails on a dirty tree even with --any-branch — an uncommitted changeset won't preview.
Conventions
- Commit messages: short imperative subject; no required prefix. Look at recent
git logfor tone. - PRs: when a change supersedes an older PR (this repo has a long tail), attribute the original author via the changeset's
creditfield — see "Adding a changeset".creditis for a prior PR, never the issue being fixed. - Don't add comments that just restate the code. Don't add backwards-compat shims for code that hasn't shipped yet.