Closes#18381
* [Changelog for Vite 7.0.0
(2025-06-24)](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md#700-2025-06-24)
Starting from Vite 7, Node 18 support will be dropped, which doesn't
really affect Tailwind. It might be worth mentioning in the
documentation that the recommended minimum Node versions are 20.19 and
22.12.
Vite 7 is only available in ESM format, which is also not an issue.
Vite's browser support aligns with the v4 guidelines:
```
Chrome 87 → 107 (tw: 111)
Edge 88 → 107 (tw: 111)
Firefox 78 → 104 (tw: 128)
Safari 14.0 → 16.0 (tw: 16.4)
```
* [Vite 7 - Browser
Support](https://vite.dev/guide/migration.html#default-browser-target-change)
* [Tailwind CSS v4 - Browser
Support](https://tailwindcss.com/docs/compatibility#browser-support)
So, at first glance, there's nothing more to do except enabling support
for these versions.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Closes#17339
This PR fixes an issue that caused changes to `@import`-ed CSS files to
no longer rebuild the stylesheet after a change was made to a template
file.
The change in the template file causes a fast-path in the Vite plugin
now after changes in 4.0.8: _partial rebuilds_. For that branch we do
not need to re-evaluate your input CSS since we know only the candidate
list changed. However, we still need to emit all build dependencies as
via `addWatchFile(…)`, otherwise Vite will not correctly register
updates for these dependencies anymore.
## Test plan
- Updated the kitchen-sink Vite update tests to ensure that an
`@import`-ed CSS file can be updated even after a partial rebuild.
- Ensure this works in our Vite playground
Fixes#16725
When using `@reference "tailwindcss";` inside a separate CSS root (e.g.
Svelte `<style>` components, CSS modules, etc.), we have no guarantee
that the CSS variables will be defined in the main stylesheet (or if
there even is one). To work around potential issues with this we decided
in #16676 that we would emit all used CSS variables from the `@theme`
inside the `@reference` block.
However, this is not only a bit surprising but also unexpected in CSS
modules and Next.js that **requires CSS module files to only create
scope-able declarations**. To fix this issue, we decided to not emit CSS
variables but instead ensure all `var(…)` calls we create for theme
values in reference mode will simply have their fallback value added.
This ensures styles work as-expected even if the root Tailwind file does
not pick up the variable as being used or _if you don't add a root at
all_. Furthermore we do not duplicate any variable declarations across
your stylesheets and you still have the ability to change variables at
runtime.
## Test plan
- Updated snapshots everywhere (see diff)
- New Next.js CSS modules integration test
Alternative to #16425Fixes#16585Fixes#16389Fixes#16252Fixes#15794Fixes#16646Fixes#16358
This PR changes the Vite plugin to use the file-system to discover
potential class names instead of relying on the module-graph. This comes
after a lot of testing and various issue reports where builds that span
different Vite instances were missing class names.
Because we now scan for candidates using the file-system, we can also
remove a lot of the bookkeeping necessary to make production builds and
development builds work as we no longer have to change the resulting
stylesheet based on the `transform` callbacks of other files that might
happen later.
This change comes at a small performance penalty that is noticeable
especially on very large projects with many files to scan. However, we
offset that change by fixing an issue that I found in the current Vite
integration that did a needless rebuild of the whole Tailwind root
whenever any source file changed. Because of how impactful this change
is, I expect many normal to medium sized projects to actually see a
performance improvement after these changes. Furthermore we do plan to
continue to use the module-graph to further improve the performance in
dev mode.
## Test plan
- Added new integration tests with cases found across the issues above.
- Manual testing by adding a local version of the Vite plugin to repos
from the issue list above and the [tailwindcss
playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
Fixes#16233
Vite has a number of special parameters that can be appended to `.css`
files that make it actually load as a JavaScript module. One such
parameter that we haven't handled before is the `?commonjs-proxy` flag.
When importing e.g. `plotly.js/lib/core`, the dependency tree would
eventually load a file called `*.css?commonjs-proxy`. We previously
scanned this for candidates even though it was not, in-fact, a
stylesheet.
This PR fixes this by adding the `?commonjs-proxy` to the ignore list. I
have also updated `SPECIAL_QUERY_RE` to more closely match the Vite
implementation. It does seem like this was the only condition we were
missing, though:
2b2299cbac/packages/vite/src/node/plugins/css.ts (L511-L517)
## Test plan
Add and import `plotly.js/lib/core` into a Vite app. I also added an
integration test to do that.
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR improves the integration tests in two ways:
1. Make the integration tests more reliable and thus less flakey
2. Make the integration tests faster (by introducing concurrency)
Tried a lot of different things to make sure that these tests are fast
and stable.
---
The biggest issue we noticed is that some tests are flakey, these are
tests with long running dev-mode processes where watchers are being used
and/or dev servers are created.
To solve this, all the tests that spawn a process look at stdout/stderr
and wait for a message from the process to know whether we can start
making changes.
For example, in case of an Astro project, you get a `watching for file
changes` message. In case of Nuxt project you can wait for an `server
warmed up in` and in case of Next.js there is a `Ready in` message.
These depend on the tools being used, so this is hardcoded per test
instead of a magically automatic solution.
These messages allow us to wait until all the initial necessary work,
internal watchers and/or dev servers are setup before we start making
changes to the files and/or request CSS stylesheets before the server(s)
are ready.
---
Another improvement is how we setup the dev servers. Before, we used to
try and get a free port on the system and use a `--port` flag or a
`PORT` environment variable. Instead of doing this (which is slow), we
rely on the process itself to show a URL with a port. Basically all
tools will try to find a free port if the default port is in use. We can
then use the stdout/stderr messages to get the URL and the port to use.
To reduce the amount of potential conflicts in ports, we used to run
every test and every file sequentially to basically guarantee that ports
are free. With this new approach where we rely on the process, I noticed
that we don't really run into this issue again (I reran the tests
multiple times and they were always stable)
<img width="316" alt="image"
src="https://github.com/user-attachments/assets/b75ddab4-f919-4995-85d0-f212b603e5c2"
/>
Note: these tests run Linux, Windows and macOS in this branch just for
testing purposes. Once this is done, we will only run Linux tests on PRs
and run all 3 of them on the `next` branch.
We do make the tests concurrent by default now, which in theory means
that there could be conflicts (which in practice means that the process
has to do a few more tries to find a free port). To reduce these
conflicts, we split up the integration tests such that Vite, PostCSS,
CLI, … tests all run in a separate job in the GitHub actions workflow.
<img width="312" alt="image"
src="https://github.com/user-attachments/assets/fe9a58a1-98eb-4d9b-8845-a7c8a7af5766"
/>
Comparing this branch against the `next` branch, this is what CI looks
like right now:
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="594" alt="image"
src="https://github.com/user-attachments/assets/540d21eb-ab03-42e8-9f6f-b3a071fc7635"
/> | <img width="672" alt="image"
src="https://github.com/user-attachments/assets/8ef2e891-08a1-464b-9954-4153174ebce7"
/> |
There also was a point in time where I introduced sequential tests such
that all spawned processes still run after each other, but so far I
didn't run into issues if we keep them concurrent so I dropped that
code.
Some small changes I made to make things more reliable:
1. When relying on stdout/stderr messages, we split lines on `\n` and we
strip all the ANSI escapes which allows us to not worry about special
ANSI characters when finding the URL or a specific message to wait for.
2. Once a test is done, we `child.kill()` the spawned process. If that
doesn't work, for whatever reason, we run a `child.kill('SIGKILL')` to
force kill the process. This could technically lead to some memory or
files not being cleaned up properly, but once CI is done, everything is
thrown away anyway.
3. As you can see in the screenshots, I used some nicer names for the
workflows.
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="276" alt="image"
src="https://github.com/user-attachments/assets/e574bb53-e21b-4619-9cdb-515431b255b9"
/> | <img width="179" alt="image"
src="https://github.com/user-attachments/assets/8bc75119-fb91-4500-a1d0-bd09f74c93ad"
/> |
They also look a bit nicer in the PR overview as well:
<img width="929" alt="image"
src="https://github.com/user-attachments/assets/04fc71fc-74b0-4e7c-9047-2aada664efef"
/>
The very last commit just filters out Windows and macOS tests again for
PRs (but they are executed on the `next` branch.
---
### Nest steps
I think for now we are in a pretty good state, but there are some things
we can do to further improve everything (mainly make things faster) but
aren't necessary. I also ran into issue while trying it so there is more
work to do.
1. More splits — instead of having a Vite folder and PostCSS folder, we
can go a step further and have folders for Next.js, Astro, Nuxt, Remix,
…
2. Caching — right now we have to run the build step for every OS on
every "job". We can re-use the work here by introducing a setup job that
the other jobs rely on. @thecrypticace and I tried it already, but were
running into some Bun specific Standalone CLI issues when doing that.
3. Remote caching — we could re-enable remote caching such that the
`build` step can be full turbo (e.g.: after a PR is merged in `next` and
we run everything again)
This PR updates all our Vite dependencies to the newly released v6.
Nothing changed in our released Vite extension so this does not need a
user-facing changelog.
## Test Plan
The Vite playground still works. Furthermore we have a pretty extensive
Vite integration test suite that now runs on v6.
This PR changes how we render `var(...)` calls for theme values,
removing the fallback values we were previously including.
```diff
.text-white {
- color: var(--color-white, #fff);
+ color: var(--color-white);
}
```
We previously included the fallbacks only so you could see the value in
dev tools but this feels like a bad reason to bloat the CSS. I'd rather
just convince the Chrome team to surface this stuff better in dev tools
in the first place.
---------
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
This PR fixes an issue where a `@source` crashes when the path
eventually resolves to a path ending in `..`.
We have to make sure that we canonicalize the path to make sure that we
are working with the real directory.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
This PR introduces a new `source(…)` argument and improves on the
existing `@source`. The goal of this PR is to make the automatic source
detection configurable, let's dig in.
By default, we will perform automatic source detection starting at the
current working directory. Auto source detection will find plain text
files (no binaries, images, ...) and will ignore git-ignored files.
If you want to start from a different directory, you can use the new
`source(…)` next to the `@import "tailwindcss/utilities"
layer(utilities) source(…)`.
E.g.:
```css
/* ./src/styles/index.css */
@import 'tailwindcss/utilities' layer(utilities) source('../../');
```
Most people won't split their source files, and will just use the simple
`@import "tailwindcss";`, because of this reason, you can use
`source(…)` on the import as well:
E.g.:
```css
/* ./src/styles/index.css */
@import 'tailwindcss' source('../../');
```
Sometimes, you want to rely on auto source detection, but also want to
look in another directory for source files. In this case, yuo can use
the `@source` directive:
```css
/* ./src/index.css */
@import 'tailwindcss';
/* Look for `blade.php` files in `../resources/views` */
@source '../resources/views/**/*.blade.php';
```
However, you don't need to specify the extension, instead you can just
point the directory and all the same automatic source detection rules
will apply.
```css
/* ./src/index.css */
@import 'tailwindcss';
@source '../resources/views';
```
If, for whatever reason, you want to disable the default source
detection feature entirely, and only want to rely on very specific glob
patterns you define, then you can disable it via `source(none)`.
```css
/* Completely disable the default auto source detection */
@import 'tailwindcss' source(none);
/* Only look at .blade.php files, nothing else */
@source "../resources/views/**/*.blade.php";
```
Note: even with `source(none)`, if your `@source` points to a directory,
then auto source detection will still be performed in that directory. If
you don't want that, then you can simply add explicit files in the globs
as seen in the previous example.
```css
/* Completely disable the default auto source detection */
@import 'tailwindcss' source(none);
/* Run auto source detection in `../resources/views` */
@source "../resources/views";
```
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Fixes: #14558
This PR fixes an issue where our Vite plugin would crash when trying to load stylesheets via certain static asset query parameters:
```ts
import raw from './style.css?raw'
import url from './style.css?url'
```
The proper behavior for our extension is to _not touch these file at all_. The `?raw` identifier should never transform anything and the `?url` one will emit a module which points to the asset URL. However, if that URL is loaded as a stylesheet, another transform hook is called and the file is properly transformed. I verified this in the Vite setup and have added an integration test ensuring these two features work as expected.
I've also greatly reduced the complexity of the Vite playground to make it easier to set up examples like this in the future.
There are still instances in which CI is flaky after #14332. This PR
applies the same fix (that is, moving the file write into the retrying
block) to all `retryAssertion` callbacks.
I noticed the Windows integration tests for Vite's `watch` mode have
been flaky. Moving the file write into the retrying assertion callback
seems to fix it and allows us to get rid of the arbitrary timeout (I
don't remember that I ever added this in the first place 😅 ).
Fixes#14205Fixes#14106
This PR reworks the Vite extension in order to supprt `lightningcss` as
the pre-processor, enable faster rebuilds, and adds support for `vite
build --watch` mode. To make this change possible, we've done two major
changes to the extension that have caused the other changes.
## 1. Tailwind CSS is a preprocessor
We now run all of our modifications in `enforce: 'pre'`. This means that
Tailwind CSS now gets the untransformed CSS files rather than the one
already going through postcss or lightningcss. We do this because
Tailwind CSS _is_ a preprocessor at the same level as those tools and we
do sometimes use the language in ways that [creates problems when it's
the input for other
bundlers](https://github.com/tailwindlabs/tailwindcss/pull/14269).
The correct solution here is to make Tailwind not depend on any other
transformations. The main reason we were not using the `enforce: 'pre'`
phase in Vite before was becuase we relied on the `@import` flattening
of postcss so we now have to do this manually. `@import` flattening is
now a concern that every Tailwind V4 client has to deal with so this
might actually be something we want to inline into tailwindcss in the
future.
## 2. A Vite config can have multiple Tailwind roots
This is something that we have not made very explicit in the previous
iteration of the Vite plugin but we have to support multiple Tailwind
roots in a single Vite workspace. A Tailwind root is a CSS file that is
used to configure Tailwind. Technically, any CSS file can be the input
for `tailwindcss` but you have to add certain rules (e.g. `@import
"tailwindcss";`) to make the compiler do something.
A workspace can have multiple of these rules (e.g. by having different
Tailwind configures for different sub-pages). With the addition of
[support for `@source`
rules](https://github.com/tailwindlabs/tailwindcss/pull/14078) and [JS
config files](https://github.com/tailwindlabs/tailwindcss/pull/14239),
Tailwind roots become more complex and can have a custom list of
_dependencies_ (that is other JavaScript modules the compiler includes
as part of these new rules). In order to _only rebuild the roots we need
to_, we have to make this separation very clear.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
When you configure custom content globs inside an `@config` file, we
want to tread these globs as being relative to that config file and not
the CSS file that requires the content file. A config can be used by
multiple CSS configs.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
This PR fixes an issue introduced with the changed candidate cache
behavior in #14187.
Prior to #14187, candidates were cached globally within an instance of
Oxide. This meant that once a candidate was discovered, it would not
reset until you either manually cleared the cache or restarted the Oxide
process. With the changes in #14187 however, the cache was scoped to the
instance of the `Scanner` class with the intention of making the caching
behavior more easy to understand and to avoid a global cache.
This, however, had an unforeseen side-effect in our Vite extension.
Vite, in dev mode, discovers files _lazily_. So when a developer goes to
`/index.html` the first time, we will scan the `/index.html` file for
Tailwind candidates and then build a CSS file with those candidate. When
they go to `/about.html` later, we will _append_ the candidates from the
new file and so forth.
The problem now arises when the dev server detects changes to the input
CSS file. This requires us to do a re-scan of that CSS file which, after
#14187, caused the candidate cache to be gone. This is usually fine
since we would just scan files again for the changed candidate list but
in the Vite case we would only get the input CSS file change _but no
subsequent change events for all other files, including those currently
rendered in the browser_). This caused updates to the CSS file to remove
all candidates from the CSS file again.
Ideally, we can separate between two concepts: The candidate cache and
the CSS input file scan. An instance of the `Scanner` could re-parse the
input CSS file without having to throw away previous candidates. This,
however, would have another issue with the current Vite extension where
we do not properly retain instances of the `Scanner` class anyways. To
properly improve the cache behavior, we will have to fix the Vite
`Scanner` retaining behavior first. Unfortunately this means that for
the short term, we have to add some manual bookkeeping to the Vite
client and retain the candidate cache between builds ourselves.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Using the [new integration test
setup](https://github.com/tailwindlabs/tailwindcss/pull/14089), this PR
adds a test for a V4 Next.js setup using the Postcss plugin. It's
testing both a full build and the dev mode (non-turbo for now).
Because of webpack, tests are quite slow which is worrisome since we
probably need to add many more integrations in the future. One idea I
have is that we separate tests in two buckets: _essential_ tests that
run often and are fast and advanced suites that we only run on CI via
custom, non-blocking, jobs.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
This PR is an umbrella PR where we will add support for the new
`@source` directive. This will allow you to add explicit content glob
patterns if you want to look for Tailwind classes in other files that
are not automatically detected yet.
Right now this is an addition to the existing auto content detection
that is automatically enabled in the `@tailwindcss/postcss` and
`@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use
the auto content detection, but uses the module graph instead.
From an API perspective there is not a lot going on. There are only a
few things that you have to know when using the `@source` directive, and
you probably already know the rules:
1. You can use multiple `@source` directives if you want.
2. The `@source` accepts a glob pattern so that you can match multiple
files at once
3. The pattern is relative to the current file you are in
4. The pattern includes all files it is matching, even git ignored files
1. The motivation for this is so that you can explicitly point to a
`node_modules` folder if you want to look at `node_modules` for whatever
reason.
6. Right now we don't support negative globs (starting with a `!`) yet,
that will be available in the near future.
Usage example:
```css
/* ./src/input.css */
@import "tailwindcss";
@source "../laravel/resources/views/**/*.blade.php";
@source "../../packages/monorepo-package/**/*.js";
```
It looks like the PR introduced a lot of changes, but this is a side
effect of all the other plumbing work we had to do to make this work.
For example:
1. We added dedicated integration tests that run on Linux and Windows in
CI (just to make sure that all the `path` logic is correct)
2. We Have to make sure that the glob patterns are always correct even
if you are using `@import` in your CSS and use `@source` in an imported
file. This is because we receive the flattened CSS contents where all
`@import`s are inlined.
3. We have to make sure that we also listen for changes in the files
that match any of these patterns and trigger a rebuild.
PRs:
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14063
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14085
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14079
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14067
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14076
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14080
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14127
- [x] https://github.com/tailwindlabs/tailwindcss/pull/14135
Once all the PRs are merged, then this umbrella PR can be merged.
> [!IMPORTANT]
> Make sure to merge this without rebasing such that each individual PR
ends up on the main branch.
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
This PR adds a new root `/integrations` folder that will be the home of
integration tests. The idea of these tests is to use Tailwind in various
setups just like our users would (by only using the publishable npm
builds).
To avoid issues with concurrent tests making changes to the file system,
to make it very easy to test through a range of versions, and to avoid
changing configuration objects over and over in test runs, we decided to
inline the scaffolding completely into the test file and have no
examples checked into the repo.
Here's an example of how this can look like for a simple Vite test:
```ts
test('works with production builds', {
fs: {
'package.json': json`
{
"type": "module",
"dependencies": {
"@tailwindcss/vite": "workspace:^",
"tailwindcss": "workspace:^"
},
"devDependencies": {
"vite": "^5.3.5"
}
}
`,
'vite.config.ts': ts`
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'
export default defineConfig({
build: { cssMinify: false },
plugins: [tailwindcss()],
})
`,
'index.html': html`
<head>
<link rel="stylesheet" href="./src/index.css">
</head>
<body>
<div class="underline m-2">Hello, world!</div>
</body>
`,
'src/index.css': css`
@import 'tailwindcss/theme' reference;
@import 'tailwindcss/utilities';
`,
},
},
async ({ fs, exec }) => {
await exec('pnpm vite build')
expect.assertions(2)
for (let [path, content] of await fs.glob('dist/**/*.css')) {
expect(path).toMatch(/\.css$/)
expect(stripTailwindComment(content)).toMatchInlineSnapshot(
`
".m-2 {
margin: var(--spacing-2, .5rem);
}
.underline {
text-decoration-line: underline;
}"
`,
)
}
},
)
```
By defining all dependencies this way, we never have to worry about
which fixtures are checked in and can more easily describe changes to
the setup.
For ergonomics, we've also added the [`embed` prettier
plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will
mean that files inlined in the `fs` setup are properly indented. No
extra work needed!
If you're using VS Code, I can also recommend the [Language
Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals)
extension so that syntax highlighting also _just works_.
A neat feature of inlining the scaffolding like this is to make it very
simple to test through a variety of versions. For example, here's how we
can set up a test against Vite 5 and Vite 4:
```js
;['^4.5.3', '^5.3.5'].forEach(viteVersion => {
test(`works with production builds for Vite ${viteVersion}`, {
fs: {
'package.json': json`
{
"type": "module",
"devDependencies": {
"vite": "${viteVersion}"
}
}
`,
async () => {
// Do something
},
)
})
```
## Philosophy
Before we dive into the specifics, I want to clearly state the design
considerations we have chosen for this new test suite:
- All file mutations should be done in temp folders, nothing should ever
mess with your working directory
- Windows as a first-class citizen
- Have a clean and simple API that describes the test setup only using
public APIs
- Focus on reliability (make sure cleanup scripts work and are tolerant
to various error scenarios)
- If a user reports an issue with a specific configuration, we want to
be able to reproduce them with integration tests, no matter how obscure
the setup (this means the test need to be in control of most of the
variables)
- Tests should be reasonably fast (obviously this depends on the
integration. If we use a slow build tool, we can't magically speed it
up, but our overhead should be minimal).
## How it works
The current implementation provides a custom `test` helper function
that, when used, sets up the environment according to the configuration.
It'll create a new temporary directory and create all files, ensuring
things like proper `\r\n` line endings on Windows.
We do have to patch the `package.json` specifically, since we can not
use public versions of the tailwindcss packages as we want to be able to
test against a development build. To make this happen, every `pnpm
build` run now creates tarballs of the npm modules (that contain only
the files that would also in the published build). We then patch the
`package.json` to rewrite `workspace:^` versions to link to those
tarballs. We found this to work reliably on Windows and macOS as well as
being fast enough to not cause any issues. Furthermore we also decided
to use `pnpm` as the version manager for integration tests because of
it's global module cache (so installing `vite` is fast as soon as you
installed it once).
The test function will receive a few utilities that it can use to more
easily interact with the temp dir. One example is a `fs.glob` function
that you can use to easily find files in eventual `dist/` directories or
helpers around `spawn` and `exec` that make sure that processes are
cleaned up correctly.
Because we use tarballs from our build dependencies, working on changes
requires a workflow where you run `pnpm build` before running `pnpm
test:integrations`. However it also means we can run clients like our
CLI client with no additional overhead—just install the dependency like
any user would and set up your test cases this way.
## Test plan
This PR also includes two Vite specific integration tests: One testing a
static build (`pnpm vite build`) and one a dev mode build (`pnpm vite
dev`) that also makes changes to the file system and asserts that the
resources properly update.
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>