Guidelines for AI agents working on the Vercel monorepo.
This is a pnpm monorepo containing 44+ packages for the Vercel CLI and runtimes:
/packages/*- Public npm packages (@vercel scope)/internals/*- Internal shared packages (@vercel-internals scope)/crates- Rust workspace/examples- Framework examples for testing/utils- Build and test utilities
pnpm install # Install dependencies
pnpm build # Build all packages
pnpm type-check # TypeScript validation
pnpm lint # ESLint check
pnpm test-unit # Run unit tests
pnpm test-e2e # Run e2e testsRun tests for a specific package:
cd packages/<name>
pnpm test-unitAlways create a changeset for all PRs.
pnpm changesetA changeset is a markdown file in .changeset/ with YAML frontmatter listing affected packages and their bump type (patch/minor/major).
- Every PR must include a changeset (use empty frontmatter for non-package changes).
- If your change modifies a package in
/packages/*, include it in the changeset frontmatter - If your change only affects non-package files (docs, config, examples, internal tooling), create a changeset with empty frontmatter - just the description
- Packages in
/internals/*,/api, and/examplesare ignored by changesets (see.changeset/config.json)
Example changeset for a package change:
---
'@vercel/node': patch
---
Fixed edge case in serverless function bundling.Example changeset for non-package changes:
---
---
Updated CI workflow configuration.- Formatting: Prettier with single quotes, trailing commas (es5), no parens for single arrow params
- Linting: ESLint with TypeScript rules
- No unused variables:
@typescript-eslint/no-unused-varsis enforced - No focused/disabled tests:
jest/no-focused-testsandjest/no-disabled-testsare errors - Use
slice()oversubstr(): substr is deprecated
Run before committing:
pnpm prettier --write .
pnpm lint- Unit tests: Jest or Vitest, located in
packages/<name>/test/unit/ - E2E tests: Fixture-based, test real deployments
- Affected testing: CI only runs tests for changed packages and dependents
When adding tests:
// Vitest style
import { describe, test, expect } from 'vitest';
describe('feature', () => {
test('should work', () => {
expect(true).toBe(true);
});
});Each package follows this structure:
packages/<name>/
├── src/
│ └── index.ts # Main entry
├── test/
├── package.json
├── tsconfig.json # Extends ../../tsconfig.base.json
└── build.mjs # esbuild config (if applicable)
Workspace dependencies use workspace:*:
{
"dependencies": {
"@vercel/build-utils": "workspace:*"
}
}When creating a new package, configure it for public npm access:
{
"name": "@vercel/new-package",
"publishConfig": {
"access": "public"
}
}Runtime packages (node, python, go, ruby, rust) implement the Builder API:
export const version = 3;
export async function build(options: BuildOptions): Promise<BuildResult> {
// Build implementation
}
// Optional exports
export async function prepareCache(
options: PrepareCacheOptions
): Promise<Files> {}
export async function startDevServer(
options: StartDevServerOptions
): Promise<StartDevServerResult> {}Test the CLI against an external repository:
cd packages/cli
pnpm vercel --cwd /path/to/external/repoThis runs your local CLI build against any project without needing to install it globally.
- Don't use
console.login CLI package -no-consolerule is enforced there - Don't skip CI hooks - Lint and type checks run in pre-commit
- Don't forget type-check - Run
pnpm type-checkbefore pushing - Don't modify examples for testing - They're used for integration tests