Skip to content

Commit d4f820a

Browse files
authored
Merge branch 'main' into push-rzoxxqrnruku
2 parents 2f09b86 + 562b82f commit d4f820a

2 files changed

Lines changed: 153 additions & 1 deletion

File tree

docs/latest/advanced/vite.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,158 @@ changes to components, islands, and CSS are reflected in the browser instantly
8585
without a full page reload. This is powered by Prefresh, Preact's fast refresh
8686
implementation.
8787

88+
## Migrating from the Builder to Vite
89+
90+
If your Fresh 2 project was created with `--builder` (or predates the Vite
91+
plugin), it uses the legacy [`Builder`](/docs/advanced/builder) class wired up
92+
in `dev.ts`. Migrating to Vite is mostly a matter of swapping `dev.ts` for a
93+
`vite.config.ts`, moving CSS into the module graph, and updating `deno.json`.
94+
95+
### 1. Update `deno.json`
96+
97+
Add the Vite plugin and `vite` itself to your imports, drop the Builder-only
98+
Tailwind packages (if any), and point `compilerOptions.types` at Vite's client
99+
types so HMR and asset imports type-check:
100+
101+
```diff deno.json
102+
{
103+
"nodeModulesDir": "manual",
104+
"tasks": {
105+
- "dev": "deno run -A --watch=static/,routes/ dev.ts",
106+
- "build": "deno run -A dev.ts build",
107+
+ "dev": "vite",
108+
+ "build": "vite build",
109+
"start": "deno serve -A _fresh/server.js"
110+
},
111+
"imports": {
112+
"fresh": "jsr:@fresh/core@^2",
113+
"preact": "npm:preact@^10",
114+
"@preact/signals": "npm:@preact/signals@^2",
115+
+ "@fresh/plugin-vite": "jsr:@fresh/plugin-vite@^1",
116+
+ "vite": "npm:vite@^7",
117+
+ "@types/babel__core": "npm:@types/babel__core@^7"
118+
},
119+
"compilerOptions": {
120+
"jsx": "precompile",
121+
"jsxImportSource": "preact",
122+
+ "types": ["vite/client"]
123+
}
124+
}
125+
```
126+
127+
If you were using `@fresh/plugin-tailwind` / `@fresh/plugin-tailwindcss-v3`,
128+
remove those imports — Vite has a first-party Tailwind plugin (see step 4).
129+
130+
### 2. Replace `dev.ts` with `vite.config.ts`
131+
132+
Delete `dev.ts` and create a `vite.config.ts` at the project root:
133+
134+
```ts vite.config.ts
135+
import { defineConfig } from "vite";
136+
import { fresh } from "@fresh/plugin-vite";
137+
138+
export default defineConfig({
139+
plugins: [fresh()],
140+
});
141+
```
142+
143+
If you passed options to `new Builder({ ... })` (custom `serverEntry`,
144+
`islandDir`, `routeDir`, `staticDir`, `ignore`), pass the equivalent options to
145+
`fresh({ ... })` — the names match. See [Configuration](#configuration) above.
146+
147+
Any `builder.registerIsland("jsr:@scope/pkg/Island.tsx")` calls become
148+
`fresh({ islandSpecifiers: ["jsr:@scope/pkg/Island.tsx"] })`.
149+
150+
### 3. Add a `client.ts` entry
151+
152+
The Builder discovered CSS by scanning `static/`. Vite needs CSS to be part of
153+
the module graph so it can hash, bundle, and hot-reload it. Move your stylesheet
154+
out of `static/` and import it from a new `client.ts` file:
155+
156+
```diff Project structure
157+
<project root>
158+
- ├── static/styles.css
159+
+ ├── assets/styles.css
160+
+ ├── client.ts
161+
├── vite.config.ts
162+
└── main.ts
163+
```
164+
165+
```ts client.ts
166+
// Import CSS files here for hot module reloading to work.
167+
import "./assets/styles.css";
168+
```
169+
170+
Then remove the manual `<link>` from your app wrapper — Vite injects the
171+
stylesheet for you:
172+
173+
```diff routes/_app.tsx
174+
<head>
175+
<meta charset="utf-8" />
176+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
177+
<title>My App</title>
178+
- <link rel="stylesheet" href="/styles.css" />
179+
</head>
180+
```
181+
182+
Static assets that are not part of the JS/CSS graph (favicons, images served by
183+
URL, robots.txt, …) stay in `static/`.
184+
185+
### 4. Switch the Tailwind plugin (if applicable)
186+
187+
Replace the Builder-side Tailwind plugin with the official Vite plugin:
188+
189+
```diff deno.json
190+
"imports": {
191+
- "@fresh/plugin-tailwind": "jsr:@fresh/plugin-tailwind@^1",
192+
- "@tailwindcss/postcss": "npm:@tailwindcss/postcss@^4",
193+
- "postcss": "npm:postcss@^8",
194+
+ "@tailwindcss/vite": "npm:@tailwindcss/vite@^4",
195+
"tailwindcss": "npm:tailwindcss@^4"
196+
}
197+
```
198+
199+
```ts vite.config.ts
200+
import { defineConfig } from "vite";
201+
import { fresh } from "@fresh/plugin-vite";
202+
import tailwindcss from "@tailwindcss/vite";
203+
204+
export default defineConfig({
205+
plugins: [fresh(), tailwindcss()],
206+
});
207+
```
208+
209+
Make sure your stylesheet starts with `@import "tailwindcss";` and is imported
210+
from `client.ts`.
211+
212+
### 5. Verify
213+
214+
Run `deno install` to pull in the new npm packages, then:
215+
216+
```sh Terminal
217+
deno task dev # starts Vite with HMR
218+
deno task build # writes _fresh/server.js and client assets
219+
deno task start # deno serve -A _fresh/server.js
220+
```
221+
222+
The output layout under `_fresh/` is the same as the Builder produced, so
223+
deployment configuration (Deno Deploy, Docker, `deno compile`) does not need to
224+
change.
225+
226+
### Checklist
227+
228+
- [ ] `dev.ts` removed, `vite.config.ts` added
229+
- [ ] `client.ts` created and imports your CSS
230+
- [ ] Stylesheet moved out of `static/` and the `<link>` removed from `_app.tsx`
231+
- [ ] `deno.json` tasks point at `vite` / `vite build`
232+
- [ ] `@fresh/plugin-vite`, `vite`, and `@types/babel__core` in `imports`
233+
- [ ] `"vite/client"` in `compilerOptions.types`
234+
- [ ] Tailwind (if used) switched to `@tailwindcss/vite`
235+
236+
> [info]: If you get stuck, run `deno run -Ar jsr:@fresh/init` in a scratch
237+
> directory and diff the generated project against yours — the generator is the
238+
> source of truth for a working Vite-based Fresh setup.
239+
88240
## Debugging
89241

90242
To debug Vite resolution issues, run Vite with the `--debug` flag:

packages/fresh/src/runtime/client/dev_hmr.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { IS_BROWSER } from "../shared.ts";
33
let ws: WebSocket;
44
let revision = 0;
55

6-
let reconnectTimer: number;
6+
let reconnectTimer: ReturnType<typeof setTimeout>;
77
const backoff = [
88
// Wait 100ms initially, because we could also be
99
// disconnected because of a form submit.

0 commit comments

Comments
 (0)