CodeVetter · Repo Unpack · Contributor handoff
tailwindlabs/tailwindcss
Read the core compiler before touching anything: `packages/tailwindcss/src/index.ts` (`parseCss` → `compileAst` → `compile`), then `design-system.ts` and `compile.ts`. Almost every behavioral change belongs in `packages/tailwindcss` or `crates/oxide`, not in the integration packages — those are thin and near-duplicative on purpose. Conventions are enforced mechanically: Prettier config lives inline in the root `package.json` (no semicolons, single quotes, 100 columns, organize-imports), and `pnpm lint` runs `prettier --check` plus `turbo lint` (which is `tsc --noEmit` per package). The project explicitly does not accept feature PRs without prior discussion, and does not accept any PR without tests.
- Read first, in order: `packages/tailwindcss/src/index.ts` (the at-rule walk and `build()` contract), `packages/tailwindcss/src/design-system.ts` (the `DesignSystem` type and its caches), `packages/tailwindcss/src/compile.ts` (candidate → AST + sorting), and `crates/oxide/src/scanner/mod.rs` (the scan lifecycle).
- Setup is `pnpm install`, `rustup default stable`, `rustup target add wasm32-wasip1-threads`, then `pnpm build`; the WASM target is mandatory because `@tailwindcss/oxide`'s `build` script always runs `build:wasm` after `build:platform`.
- Day-to-day loop is `pnpm tdd` (Vitest watch with `--hideSkippedTests`); for browser or integration behavior you must run `pnpm build` first because both harnesses consume built artifacts.
- Before submitting: run `pnpm test` (cargo + vitest), `pnpm build && pnpm test:integrations`, `pnpm build && pnpm test:ui`, and `pnpm run lint`; `pnpm run format` auto-fixes style.
- Code style is Prettier with `semi: false`, `singleQuote: true`, `printWidth: 100`, and `prettier-plugin-organize-imports`; `integrations/**/*.ts` additionally uses `prettier-plugin-embed` so embedded CSS/HTML template literals are formatted.
- Safe edit boundary: the core compiler must stay IO-free. Anything needing `fs`, module resolution, or Lightning CSS belongs in `@tailwindcss/node`, which is the only package allowed to be the bridge between core and the runtime.
- A change to utility or variant output almost always needs a snapshot update in `packages/tailwindcss/src/utilities.test.ts` or `variants.test.ts`, plus a check of `packages/tailwindcss/src/__snapshots__/`; these files are enormous, so target specific test names rather than running the whole suite.
- Trap: output ordering is not alphabetical. It is governed by `GLOBAL_PROPERTY_ORDER` in `property-order.ts` combined with a BigInt variant bitmask; reordering that array silently reshuffles every generated stylesheet.
- Trap: `process.env.NODE_ENV !== 'test'` gates both the license banner comment prepended in `compileAst` and the Lightning CSS warning output, so CSS produced under test differs from production output.
- Trap: the integration packages duplicate the same source-derivation block (`root === 'none'` / `root === null` / explicit root, concat `compiler.sources`). Changing that contract in core requires updating the CLI, Vite, PostCSS, webpack, and Turbopack call sites together.
- Trap: Rust edits need `pnpm build` (or `pnpm --filter @tailwindcss/oxide build`) to regenerate the `.node` addon before JS tests see them; `turbo.json` declares the cross-crate `inputs` that make this cache correctly.
- Debugging hook: set `DEBUG=1` (or `DEBUG=tailwindcss`) to enable the `Instrumentation` timers that bracket every phase — 'Setup compiler', 'Scan for candidates', 'Build CSS', 'Optimize CSS' — across the CLI, Vite, PostCSS, and loaders.
- Debugging hook for the scanner: `crates/oxide/src/scanner/init_tracing.rs` gates `tracing` events behind a `SHOULD_TRACE` lazy static, logging both provided and optimized source entries at INFO level.
- Process convention: PRs need a test plan and tests; feature PRs are likely to be closed without prior discussion in GitHub Discussions; adding `[ci-all]` to the PR body opts into the full Windows/macOS CI matrix.
- Do not hand-edit release plumbing: `scripts/version-packages.mjs`, `scripts/lock-pre-release-versions.mjs`, `scripts/release-channel.js`, and `scripts/pre-publish-optimizations.mjs` are driven by `prepare-release.yml`/`release.yml`, and npm trusted publishing validates the calling workflow filename.