Hi! We are really excited that you are interested in contributing to Vite. Before submitting your contribution, please make sure to take a moment and read through the following guide:
The Vite repo is a monorepo using pnpm workspaces. The package manager used to install and link dependencies must be pnpm.
To develop and test the core vite
package:
-
Run
pnpm i
in Vite's root folder -
Go to
packages/vite
and runpnpm run dev
. This startsrollup
in watch mode. -
Run
pnpm link --global
inpackages/vite
. This linksvite
globally so that you can:- Run
pnpm link --global vite
in another Vite project to use the locally built Vite; - Use the
vite
binary anywhere.
If your project has
vite
as a nested dependency, you can customize the dependency resolution instead depending on the package manager used. For pnpm, add this in your project's rootpackage.json
:{ "pnpm": { "overrides": { "vite": "link:../path/to/vite/packages/vite" } } }
And re-run
pnpm install
to link the package. - Run
Each package under packages/playground/
contains a __tests__
directory. The tests are run using Jest + Playwright with custom integrations to make writing tests simple. The detailed setup is inside jest.config.js
and scripts/jest*
files.
Each test can be run under either dev server mode or build mode.
-
pnpm test
by default runs every test in both serve and build mode. -
pnpm run test-serve
runs tests only under serve mode. This is just callingjest
so you can pass any Jest flags to this command. Since Jest will attempt to run tests in parallel, if your machine has many cores this may cause flaky test failures with multiple Playwright instances running at the same time. You can force the tests to run in series withpnpm run test-serve -- --runInBand
. -
pnpm run test-build
runs tests only under build mode. -
You can also use
pnpm run test-serve -- [match]
orpnpm run test-build -- [match]
to run tests in a specific playground package, e.g.pnpm run test-serve -- css
will run tests for bothplayground/css
andplayground/css-codesplit
under serve mode.Note package matching is not available for the
pnpm test
script, which always runs all tests.
Inside playground tests, a global page
object is automatically available, which is a Playwright Page
instance that has already navigated to the served page of the current playground. So writing a test is as simple as:
test('should work', async () => {
expect(await page.textContent('.foo')).toMatch('foo')
})
Some common test helpers, e.g. testDir
, isBuild
or editFile
are available in packages/playground/testUtils.ts
.
To add new tests, you should find a related playground to the fix or feature (or create a new one). As an example, static assets loading are tested in the assets playground. In this Vite App, there is a test for ?raw
imports, with a section is defined in the index.html
for it:
<h2>?raw import</h2>
<code class="raw"></code>
This will be modified with the result of a file import:
import rawSvg from './nested/fragment.svg?raw'
text('.raw', rawSvg)
Where the text
util is defined as:
function text(el, text) {
document.querySelector(el).textContent = text
}
In the spec tests, the modifications to the DOM listed above are used to test this feature:
test('?raw import', async () => {
expect(await page.textContent('.raw')).toMatch('SVG')
})
In many test cases we need to mock dependencies using link:
and file:
protocols (which are supported by package managers like yarn
and pnpm
). However, pnpm
treats link:
and file:
the same way and always use symlinks. This can be undesirable in cases where we want the dependency to be actually copied into node_modules
.
To work around this, playground packages that uses the file:
protocol should also include the following postinstall
script:
"scripts": {
//...
"postinstall": "node ../../../scripts/patchFileDeps.cjs"
}
This script patches the dependencies using file:
protocol to match the copying behavior instead of linking.
You can set the DEBUG
environment variable to turn on debugging logs. E.g. DEBUG="vite:resolve"
. To see all debug logs you can set DEBUG="vite:*"
, but be warned that it will be quite noisy. You can run grep -r "createDebugger('vite:" packages/vite/src/
to see a list of available debug scopes.
-
Checkout a topic branch from a base branch, e.g.
main
, and merge back against that branch. -
If adding a new feature:
- Add accompanying test case.
- Provide a convincing reason to add this feature. Ideally, you should open a suggestion issue first and have it approved before working on it.
-
If fixing bug:
- If you are resolving a special issue, add
(fix #xxxx[,#xxxx])
(#xxxx is the issue id) in your PR title for a better release log, e.g.fix: update entities encoding/decoding (fix #3899)
. - Provide a detailed description of the bug in the PR. Live demo preferred.
- Add appropriate test coverage if applicable.
- If you are resolving a special issue, add
-
It's OK to have multiple small commits as you work on the PR - GitHub can automatically squash them before merging.
-
Make sure tests pass!
-
Commit messages must follow the commit message convention so that changelogs can be automatically generated. Commit messages are automatically validated before commit (by invoking Git Hooks via yorkie).
-
No need to worry about code style as long as you have installed the dev dependencies - modified files are automatically formatted with Prettier on commit (by invoking Git Hooks via yorkie).
The following section is mostly for maintainers who have commit access, but it's helpful to go through if you intend to make non-trivial contributions to the codebase.
Vite aims to be lightweight, and this includes being aware of the number of npm dependencies and their size.
We use rollup to pre-bundle most dependencies before publishing! Therefore most dependencies, even used in src code, should be added under devDependencies
by default. This also creates a number of constraints that we need to be aware of in the codebase:
In some cases we intentionally lazy-require some dependencies to improve startup performance. However, note that we cannot use simple require('somedep')
calls since these are ignored in ESM files so the dependency won't be included in the bundle, and the actual dependency won't even be there when published since they are in devDependencies
.
Instead, use (await import('somedep')).default
.
Most deps should be added to devDependencies
even if they are needed at runtime. Some exceptions are:
- Type packages. Example:
@types/*
. - Deps that cannot be properly bundled due to binary files. Example:
esbuild
. - Deps that ships its own types and its type is used in vite's own public types. Example:
rollup
.
Avoid deps that has large transitive dependencies that results in bloated size compared to the functionality it provides. For example, http-proxy
itself plus @types/http-proxy
is a little over 1MB in size, but http-proxy-middleware
pulls in a ton of dependencies that makes it 7MB(!) when a minimal custom middleware on top of http-proxy
only requires a couple lines of code.
Vite aims to be fully usable as a dependency in a TypeScript project (e.g. it should provide proper typings for VitePress), and also in vite.config.ts
. This means technically a dependency whose types are exposed needs to be part of dependencies
instead of devDependencies
. However, these means we won't be able to bundle it.
To get around this, we inline some of these dependencies' types in packages/vite/types
. This way we can still expose the typing but bundle the dependency's source code.
We already have many config options, and we should avoid fixing an issue by adding yet another one. Before adding an option, try to think about:
- Whether the problem is really worth addressing
- Whether the problem can be fixed with a smarter default
- Whether the problem has workaround using existing options
- Whether the problem can be addressed with a plugin instead