Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@

"@tailwindcss/postcss": "npm:@tailwindcss/postcss@^4.1.10",
"redis": "npm:redis@^5.8.2",
"rollup": "npm:rollup@^4.55.1",
"rolldown": "npm:rolldown@^1.0.0-rc.13",
"tailwindcss": "npm:tailwindcss@^4.1.10",
"postcss": "npm:postcss@8.5.6",

Expand All @@ -95,7 +95,7 @@
"marked": "npm:marked@^15.0.11",
"marked-mangle": "npm:marked-mangle@^1.1.9",
"prismjs": "npm:prismjs@^1.29.0",
"vite": "npm:vite@^7.3.1"
"vite": "npm:vite@^8.0.3"
},
"compilerOptions": {
"lib": ["dom", "dom.asynciterable", "deno.ns", "deno.unstable"],
Expand Down
588 changes: 462 additions & 126 deletions deno.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/latest/testing/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ export const FRESH_BUILD_CONFIG: InlineConfig = {
logLevel: "error",
root: "./",
build: { emptyOutDir: true },
configLoader: "native",
environments: {
ssr: { build: { outDir: path.join("_fresh", "server") } },
client: { build: { outDir: path.join("_fresh", "client") } },
Expand Down
6 changes: 3 additions & 3 deletions packages/init/src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,8 +605,8 @@ if (Deno.args.includes("build")) {

if (useVite) {
denoJson.compilerOptions.types = ["vite/client"];
denoJson.tasks.dev = "vite";
denoJson.tasks.build = "vite build";
denoJson.tasks.dev = "vite --configLoader=native";
denoJson.tasks.build = "vite --configLoader=native build";

const vitePluginVersion = await getLatestVersion(
"@fresh/plugin-vite",
Expand All @@ -615,7 +615,7 @@ if (Deno.args.includes("build")) {

denoJson.imports["@fresh/plugin-vite"] =
`jsr:@fresh/plugin-vite@^${vitePluginVersion}`;
denoJson.imports["vite"] = "npm:vite@^7.1.3";
denoJson.imports["vite"] = "npm:vite@^8.0.3";
denoJson.imports["@types/babel__core"] = "npm:@types/babel__core@^7.20.5";

if (useTailwind) {
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-vite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ Vite plugin for [Fresh](https://fresh.deno.dev).

4. Update your deno tasks respectively:

- Dev: `vite`
- Build: `vite build`
- Dev: `vite --configLoader=native`
- Build: `vite build --configLoader=native`

More information
[on the Fresh documentation](https://fresh.deno.dev/docs/advanced/vite) .
8 changes: 4 additions & 4 deletions packages/plugin-vite/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
"types": ["vite/client"]
},
"tasks": {
"demo": "vite demo",
"debug": "deno run -A --inspect-brk npm:vite demo",
"demo:build": "vite build demo",
"demo": "vite --configLoader=native demo",
"debug": "deno run -A --inspect-brk npm:vite --configLoader=native demo",
"demo:build": "vite build --configLoader=native demo",
"demo:start": "cd demo && deno serve -A _fresh/server.js"
},
"imports": {
Expand All @@ -41,7 +41,7 @@
"qs": "npm:qs@^6.14.0",
"rollup-plugin-visualizer": "npm:rollup-plugin-visualizer@^6.0.3",
"stripe": "npm:stripe@^19.1.0",
"vite": "npm:vite@^7.1.4",
"vite": "npm:vite@^8.0.3",
"vite-plugin-inspect": "npm:vite-plugin-inspect@^11.3.2",
"vite-plugin-pwa": "npm:vite-plugin-pwa@^1.0.3"
}
Expand Down
64 changes: 64 additions & 0 deletions packages/plugin-vite/src/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ export function fresh(config?: FreshViteConfig): Plugin[] {
isDev = env.command === "serve";

return {
oxc: {
jsx: {
runtime: "automatic",
importSource: "preact",
development: env.command === "serve",
},
},
// TODO: Remove
esbuild: {
jsx: "automatic",
jsxImportSource: "preact",
Expand Down Expand Up @@ -139,6 +147,13 @@ export function fresh(config?: FreshViteConfig): Plugin[] {
? config.build.outDir + "/client"
: null) ??
"_fresh/client",
rolldownOptions: {
preserveEntrySignatures: "strict",
input: {
"client-entry": "fresh:client-entry",
},
},
// TODO: Remove
rollupOptions: {
preserveEntrySignatures: "strict",
input: {
Expand All @@ -158,6 +173,55 @@ export function fresh(config?: FreshViteConfig): Plugin[] {
? config.build.outDir + "/server"
: null) ??
"_fresh/server",
rolldownOptions: {
onwarn(warning, handler) {
// Ignore "use client"; warnings
if (warning.code === "MODULE_LEVEL_DIRECTIVE") {
return;
}

// Ignore optional export errors
if (
warning.code === "MISSING_EXPORT" &&
warning.id?.startsWith("\0fresh-route::")
) {
return;
}

// Ignore commonjs optional exports
if (
warning.code === "MISSING_EXPORT" &&
warning.message.includes("__require")
) {
return;
}

// Ignore this warnings
if (warning.code === "THIS_IS_UNDEFINED") {
return;
}

// Ignore falsy source map errors
if (warning.code === "SOURCEMAP_ERROR") {
return;
}

return handler(warning);
},
// workaround: Cannot use export statement outside a module
// https://github.com/oxc-project/oxc/blob/a4ac3ce5148c22116436f04516641cd56e67e3ae/crates/oxc_semantic/src/diagnostics.rs#L141
// https://github.com/oxc-project/oxc/blob/a4ac3ce5148c22116436f04516641cd56e67e3ae/crates/oxc_semantic/src/checker/javascript.rs#L537-L540
external: (id) => {
if (id.endsWith(".cjs")) {
return true;
}
return false;
},
input: {
"server-entry": "fresh:server_entry",
},
},
// TODO: Remove
rollupOptions: {
onwarn(warning, handler) {
// Ignore "use client"; warnings
Expand Down
11 changes: 8 additions & 3 deletions packages/plugin-vite/src/plugins/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ export function deno(): Plugin {
// But we still want to ignore everything `vite:resolve` does
// because we're kinda replacing that plugin here.
const tmp = await this.resolve(id, importer, options);
if (tmp && tmp.resolvedBy !== "vite:resolve") {
// The `resolveId` hook `resolvedBy` is not supported
// https://github.com/rolldown/rolldown/blob/2280eb2298e42835c8d1049a5b32486693c45d0e/packages/rollup-tests/src/ignored-by-unsupported-features.md?plain=1#L13-L14
// https://github.com/rolldown/rolldown/issues/8688
if (tmp) {
if (tmp.external && !/^https?:\/\//.test(tmp.id)) {
return tmp;
}
Expand Down Expand Up @@ -143,12 +146,14 @@ export function deno(): Plugin {
return null;
}

const type = getDenoType(id, options.attributes.type ?? "default");
// https://github.com/rolldown/rolldown/issues/2758
const type = getDenoType(id, options?.attributes?.type ?? "default");
if (
type !== RequestedModuleType.Default ||
/^(https?|jsr|npm):/.test(resolved)
) {
return toDenoSpecifier(resolved, type);
// [UNLOADABLE_DEPENDENCY] Error: Could not load @marvinh-test/fresh-island - No such file or directory (os error 2).
return { id: toDenoSpecifier(resolved, type) };
}

if (resolved.startsWith("file://")) {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-vite/src/plugins/verify_imports.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Plugin } from "vite";
import * as cl from "@std/fmt/colors";
import type { PluginContext } from "rollup";
import type { PluginContext } from "rolldown";
import path from "node:path";
import { pathWithRoot } from "../utils.ts";

Expand Down
2 changes: 2 additions & 0 deletions packages/plugin-vite/tests/build_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ integrationTest("vite build - import node:*", async () => {
);
});

// needs: https://github.com/vitejs/vite/pull/21580
// https://github.com/rolldown/rolldown/issues/7012
integrationTest("vite build - css modules", async () => {
await launchProd(
{ cwd: viteResult.tmp },
Expand Down
21 changes: 19 additions & 2 deletions packages/plugin-vite/tests/test_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,15 @@ export async function launchDevServer(
await withChildProcessServer(
{
cwd: dir,
args: ["run", "-A", "--cached-only", "npm:vite", "--port", "0"],
args: [
"run",
"-A",
"--cached-only",
"npm:vite",
"--configLoader=native",
"--port",
"0",
],
env,
},
async (address) => await fn(address, dir),
Expand All @@ -100,7 +108,15 @@ export async function spawnDevServer(
const server = withChildProcessServer(
{
cwd: dir,
args: ["run", "-A", "--cached-only", "npm:vite", "--port", "0"],
args: [
"run",
"-A",
"--cached-only",
"npm:vite",
"--configLoader=native",
"--port",
"0",
],
env,
},
async (address) => {
Expand Down Expand Up @@ -156,6 +172,7 @@ export async function buildVite(
build: {
emptyOutDir: true,
},
configLoader: "native",
environments: {
ssr: {
build: {
Expand Down
2 changes: 1 addition & 1 deletion tools/check_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const totalStart = performance.now();
// deno-lint-ignore no-console
console.log("Building www...");
let stepStart = performance.now();
const builder = await createBuilder({ root: www });
const builder = await createBuilder({ root: www, configLoader: "native" });
await builder.buildApp();
// deno-lint-ignore no-console
console.log(
Expand Down
136 changes: 136 additions & 0 deletions vite8-issues.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Vite 8 / Rolldown Migration Notes

This document summarizes the migration work, regressions found, fixes applied, and investigation outcomes while moving `@fresh/plugin-vite` from Vite 7/Rollup behavior to Vite 8/Rolldown behavior.

---

## Current Status

- `deno test -A packages/plugin-vite/tests/build_test.ts`
- ✅ **30 passed / 0 failed**
- Previously failing areas during migration:
- Bare/JSR island resolution (`@marvinh-test/fresh-island`)
- `.cjs` parsing failures in SSR build
- CSS Modules not applied for island/imported chunks

---

## Root Causes Identified

### 1) Resolver contract differences in Rolldown path

**Symptoms**
- Build error:
- `Could not load @marvinh-test/fresh-island - No such file or directory (os error 2)`

**Causes**
- `resolveId` result shape assumptions from previous behavior no longer held.
- Returned string IDs were not always sufficient in this path.
- `resolveId` metadata assumptions (`resolvedBy`) were invalid with current typings/runtime surface.
- `resolveId` options import attributes (`options.attributes`) are not available in current Rolldown integration (see linked Rolldown issue below).

**Fixes**
- Return object from resolver for deno namespaced IDs:
- `return { id: toDenoSpecifier(...) }`
- Removed usage of unsupported resolver provenance metadata (`resolvedBy`).
- Stopped relying on unavailable import-attributes options in resolver:
- Fallback to default type handling in plugin path.
- Result: remote island resolution now works again.

---

### 2) `.cjs` files transformed to ESM, then parsed as CommonJS

**Symptoms**
- SSR build errors such as:
- `Cannot use export statement outside a module`
- `Cannot use import statement outside a module`
- Reported on transformed `.cjs` files (e.g. demo fixtures).

**Cause**
- `fresh:patches` includes `cjsPlugin`, which rewrites CommonJS patterns to ESM-style syntax.
- Under Vite 8 / Rolldown / OXC strictness, `.cjs` is still interpreted as CommonJS by extension semantics.
- Result: transformed ESM syntax in `.cjs` became invalid for parser expectations.

**Fix**
- SSR workaround in `packages/plugin-vite/src/mod.ts`:
- Mark `.cjs` as external in `rolldownOptions.external`.
- This bypasses problematic bundling/parsing path for `.cjs` in SSR.

**Notes**
- This is a practical compatibility workaround.
- Long-term ideal is upstream/toolchain alignment of module-kind and transformed output.

---

### 3) CSS Modules missing from island/imported chunks

**Symptoms**
- `vite build - css modules` test failed (computed styles were incorrect).
- Island route rendered, but expected island CSS was missing in output.

**Cause**
- Island CSS collection logic relied on manifest assumptions that did not consistently hold after migration.
- Shared/imported chunk CSS was not always associated with island entries.
- Manifest lookup by a single key strategy was insufficient with current output shape.

**Fix**
- In `server_snapshot.ts`, switched to robust CSS collection:
- Build helper to resolve manifest chunk by reference (supports key/file matching).
- Recursively walk `imports` graph.
- Aggregate all reachable CSS into island entry CSS list.
- Result: CSS modules tests passed.

---

## Code-Level Changes (Summary)

### `packages/plugin-vite/src/plugins/deno.ts`

- Updated resolver behavior for Rolldown compatibility:
- Return object form for deno specifier IDs.
- Removed unsupported assumptions:
- No `resolvedBy` checks.
- No direct `options.attributes` usage (unavailable in current environment).
- Preserved plugin-chain interoperability while keeping deno-first resolution intent.

### `packages/plugin-vite/src/mod.ts`

- Added SSR Rolldown external rule:
- Externalize `.cjs` to avoid parser/module-kind mismatch in bundle phase.

### `packages/plugin-vite/src/plugins/server_snapshot.ts`

- Reworked island CSS gathering:
- Added manifest reference resolution helper.
- Added recursive import-graph CSS collection.
- Ensured island registry receives full CSS set including transitive imports.

---

## Related Upstream References

- Rolldown import attributes support gap:
- https://github.com/rolldown/rolldown/issues/2758
- Potentially related CSS/chunk behavior class:
- https://github.com/rolldown/rolldown/issues/7012

---

## Migration Lessons

1. **Do not rely on non-standard resolver metadata** unless guaranteed by current types/runtime.
2. **Treat `.cjs` carefully** under stricter parser semantics; avoid producing ESM syntax in `.cjs` bundle path.
3. **Assume manifest shape variance** across bundlers; use resilient chunk resolution and import-graph traversal.
4. **Prefer behavior-based guards** (e.g., `external`, virtual IDs, imports graph) over provenance-based assumptions.

---

## Remaining Follow-up (Optional)

- Revisit `.cjs` external workaround when upstream parser/module-kind behavior improves.
- Consider narrowing `cjsPlugin` application scope to avoid `.cjs` transformation hazards.
- Add targeted regression tests around:
- manifest key/file reference differences
- transitive island CSS inclusion
- deno resolver fallback behavior under Rolldown.
Loading
Loading