Skip to content

Latest commit

 

History

History
226 lines (174 loc) · 11.9 KB

CONTRIBUTING.md

File metadata and controls

226 lines (174 loc) · 11.9 KB

Contributing to Tasty

Thanks for your interest in contributing to Tasty! This document covers everything you need to get a development environment running, propose changes, and ship them.

By participating in this project you agree to abide by the Code of Conduct.

Ways to Contribute

  • Report a bug — open a GitHub issue with a minimal reproduction (CodeSandbox/StackBlitz link or a small repo is ideal).
  • Propose a feature — open an issue describing the use case before opening a PR. For non-trivial changes it's worth discussing the design first to save you time.
  • Improve the docs — typo fixes, clarifications, and new examples are all welcome. Docs live under docs/ and the project root.
  • Submit a fix or feature — see the workflow below.

For security vulnerabilities, do not open a public issue — see SECURITY.md.

Prerequisites

  • Node.js 22.19+ on the 22.x line or 24.11+ on the 24.x line for repository tooling (the published package supports Node.js 20+)
  • pnpm 11+ (the project uses packageManager: pnpm@11.x in package.json)
  • Git

Local Setup

git clone https://github.com/tenphi/tasty.git
cd tasty
pnpm install
pnpm test:setup
pnpm build
pnpm test

pnpm test:setup downloads the headless Chromium binary that part of the test suite runs against. It is a one-time step per checkout — see Testing.

Project Layout

src/                 Source code
  pipeline/          Style rendering pipeline (parse → exclusives → materialize)
  ssr/               Server-side rendering integrations
  static/            Zero-runtime entry (tastyStatic)
  zero/              Babel plugin for build-time extraction
docs/                Public documentation
scripts/             Repo automation (CI helpers)
.changeset/          Pending changelog entries

For a deeper map of the codebase, see AGENTS.md.

Common Scripts

Script Purpose
pnpm build Build all entry points with tsdown
pnpm test Run the test suite once (both projects)
pnpm test:node Run only the Node project (pure logic, no DOM)
pnpm test:browser Run only the browser project (headless Chromium)
pnpm test:setup Download the Chromium binary the browser project needs
pnpm test:watch Watch mode for tests
pnpm test:coverage Run tests with coverage
pnpm typecheck Run tsc --noEmit
pnpm lint / pnpm lint:fix Lint with ESLint
pnpm format / pnpm format:check Prettier formatting
pnpm hygiene Lint + format check + typecheck (CI mirror)
pnpm hygiene:fix Auto-fix lint + format, then typecheck
pnpm size Run size-limit checks against the built bundles
pnpm bench Run pipeline benchmarks (Node project)
pnpm bench:browser Run component-render benchmarks (headless Chromium)
pnpm bench:overhead / bench:injection / bench:interaction / bench:tree Production-React browser benchmarks (see Runtime Benchmarks)
pnpm bench:cold-start Page-load cold-start benchmark (run pnpm build first)
pnpm knip Dead-code check
pnpm check:build / check:react-compat / check:babel-compat / check:test-only Built-entry, packed React/Babel compatibility, and test-only-code checks (run after pnpm build)

Run pnpm hygiene before pushing — CI runs the same checks.

Testing

The suite is split into two Vitest projects, configured in vitest.config.ts:

Project Environment Covers
node plain Node, no DOM Parser, style handlers, pipeline, SSR string output, Babel extractor — pure logic
browser headless Chromium (Playwright) Anything that touches document, renders React, or asserts on CSS the engine actually parsed

Tasty compiles to CSS, and only a real CSS engine can tell you whether that CSS is valid. jsdom and happy-dom reject @container, @starting-style, @property, @function, and CSS nesting, which silently hollowed out the assertions that depended on them. Running those suites in Chromium means an invalid declaration shows up as a property the browser dropped.

Adding a test. Put it next to the source it exercises. If it touches document, renders React, or asserts on injected CSS, add it to the BROWSER_TESTS list in vitest.config.ts — every *.test.tsx file is already matched. Everything else runs in the node project automatically, and a DOM test that ends up there fails immediately with document is not defined.

Writing browser assertions. The engine reserializes the CSS it accepts, so prefer tolerant matchers over exact strings — oklch(var(--x)/.1) reads back as oklch(var(--x) / .1). When the point is to pin Tasty's own output verbatim, configure forceTextInjection: true and read it back with getCSSText().

Development Workflow

  1. Fork the repository and create a feature branch from main:
    git checkout -b fix/short-description
  2. Make your change. Keep PRs focused — one logical change per PR is much easier to review and ship.
  3. Add or update tests. Bug fixes should include a regression test; features should include coverage for the new behavior. Tests use Vitest and live next to the source they exercise — see Testing for which of the two projects yours belongs in.
  4. Update the docs. Public-API changes need a corresponding update under docs/ (and usually the README's relevant section).
  5. Add a changeset (see below) describing the user-facing change.
  6. Run hygiene + tests:
    pnpm hygiene
    pnpm test
  7. Commit and push. Use clear, present-tense commit messages (fix: …, feat: …, docs: …). Conventional Commits aren't enforced, but they help.
  8. Open a pull request against main. Describe the motivation, the change, and any trade-offs. Link the related issue if there is one.

Dependency upgrades

Choose the newest compatible, stable, non-deprecated release that has been published for at least 14 full days. This applies to npm packages, the pinned pnpm version, and GitHub Actions, except first-party @tenphi/* packages (see below). Verify npm's time metadata (for example, pnpm view <package> time --json) or the action's GitHub release publication date; do not infer age from a dependency PR's creation date or use an unreviewed @latest update.

Record the UTC cutoff and the selected versions and publication dates in the PR. Review major-version migration guides, Node requirements, and peer dependencies. Upgrade coupled packages together (React and React DOM, Babel, Vitest and its browser provider, Astro and its React integration, and Size Limit plugins). If the newest mature version conflicts with the supported API or tooling, use the newest compatible release and explain the hold in the PR. Keep Node types aligned with the supported LTS tooling. A tooling upgrade does not justify raising public peer minima, narrowing peer ranges, or raising the published Node requirement. Preserve existing consumer ranges unless the package actually needs a newer version; any necessary support change must be explicit in the PR and changeset, with tests against the remaining supported versions. Check packed declarations against older supported React types as well as checking runtime compatibility.

pnpm-workspace.yaml enforces 14 days (minimumReleaseAge: 20160) for direct and transitive npm dependencies, fails when no eligible version exists, and requires publication timestamps. Keep these checks enabled when refreshing the lockfile. First-party @tenphi/* packages (such as @tenphi/eslint-plugin-tasty) come from the same maintainer, so they are excluded through minimumReleaseAgeExclude and Dependabot's cooldown.exclude, and a fix in one of them can be adopted the day it ships. Dependabot uses the same 14-day cooldown for version updates and groups coupled packages and other minor/patch updates to reduce PR noise. Dependabot security updates bypass its cooldown; an urgent fix requiring a younger npm release needs a documented, version-specific minimumReleaseAgeExclude exception reviewed in the PR. Remove that exception after the release matures.

Run the full submit workflow, both test projects, and the CI checks (including the built-entrypoint, packed React and Babel compatibility, and test-only-code checks). Runtime/type dependency or generated-build changes need a changeset; tooling-only updates do not.

Changesets

Tasty uses Changesets to manage versions and the changelog. Every PR with a user-visible change must include a changeset:

pnpm changeset

The CLI walks you through selecting the bump type (patch, minor, major) and writing a short summary. The summary becomes the changelog entry, so write it for the consumer, not the implementer:

Fix overlapping selectors when default and custom-state token values coincide but other state values differ.

Internal-only changes (refactors, CI tweaks, doc-only updates) don't need a changeset — note that in the PR description.

Coding Guidelines

  • TypeScript first. Keep the public API fully typed; prefer narrow, descriptive types over any.
  • No hooks in the core. tasty(), the style functions, and SSR helpers are intentionally hook-free so they work in React Server Components; useStyles is the one exception, and it only reads context when React provides it. Don't introduce React-only runtime dependencies in src/ outside the React entry points.
  • Determinism. The selector model relies on mutually exclusive conditions. When touching the pipeline, add tests that exercise selector exclusivity, not just snapshot output.
  • Performance. The pipeline has multi-level caches (parser, state-key, simplify, condition). When changing hot paths, run pnpm bench before and after and include the numbers in the PR.
  • Bundle size. pnpm size enforces per-entry-point limits. If a change increases bundle size, justify it in the PR.
  • Prettier + ESLint. No style debates — let the tools format the code.

Documentation Style

  • Audience-first headings (the docs hub at docs/README.md is organized by role and task).
  • Short, real-world code examples over exhaustive enumerations.
  • Link to the relevant source files when describing internals.

Testing Tips

  • Unit-style tests live alongside the code they cover (*.test.ts(x)).
  • Pipeline behavior is best tested at the renderStyles boundary so you catch interactions across stages.
  • For SSR/RSC behavior, the integration tests under src/ssr/ are the source of truth.

Reviewing Pull Requests

Reviewers look for:

  • A clear description of motivation and scope
  • Tests that fail without the change and pass with it
  • Updated docs and a changeset (when user-visible)
  • Green CI: build, lint, format check, typecheck, knip, built-entry, React and Babel compatibility, and test-only-code checks, tests, size-limit
  • No unrelated changes (formatting drift, dependency churn, etc.)

PRs that touch the pipeline or SSR collectors will usually take an extra round of review — that's expected.

Releases

Releases are automated via the release workflow:

  • Merging changesets to main opens a Version Packages PR that bumps versions and rewrites CHANGELOG.md.
  • Merging that PR publishes to npm via OIDC trusted publishing and creates a GitHub Release.
  • Snapshot releases are published from PRs under the pr_<number> dist-tag for early testing.

Maintainers are responsible for merging the Version Packages PR — contributors don't need to do anything beyond adding changesets.

Getting Help

  • Open an issue for bugs, concrete proposals, design questions, or general help.

Thanks again — every contribution helps make Tasty better.