1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102import { defineConfig } from "vite";
import vinext from "vinext";
import { cloudflare } from "@cloudflare/vite-plugin";
import mdx from "@mdx-js/rollup";
import { remarkCodeHike, recmaCodeHike, type CodeHikeConfig } from "codehike/mdx";
import path from "node:path";
const codeHikeConfig: CodeHikeConfig = {
components: { code: "MyCode", inlineCode: "MyInlineCode" },
};
/**
* Vite config for the Next.js App Router Playground running on vinext.
*
* This replaces next.config.ts โ all Next.js features are provided by
* the vinext plugin + @vitejs/plugin-rsc for RSC support.
*
* To run: npx vite dev
* To build: npx vite build
* To deploy to Cloudflare: npx wrangler deploy
*/
export default defineConfig({
plugins: [
// Shim React canary APIs (ViewTransition, addTransitionType) that don't
// exist in stable React 19. Provides no-op replacements so the
// view-transitions page degrades gracefully instead of crashing.
{
name: "shim-react-canary",
resolveId(id) {
if (id === "virtual:react-with-canary") return "\0virtual:react-with-canary";
},
load(id) {
if (id === "\0virtual:react-with-canary") {
return `
export * from "react";
export { default } from "react";
import React from "react";
export const ViewTransition = React.ViewTransition || (({ children }) => children);
export const addTransitionType = React.addTransitionType || (() => {});
`;
}
},
transform(code, id) {
// Rewrite imports from 'react' that reference canary APIs
if (
!id.includes("node_modules") &&
(id.endsWith(".tsx") || id.endsWith(".ts") || id.endsWith(".jsx") || id.endsWith(".js")) &&
(code.includes("ViewTransition") || code.includes("addTransitionType")) &&
/from\s+['"]react['"]/.test(code)
) {
// Only rewrite if the import actually destructures ViewTransition or addTransitionType
const importRegex = /import\s*\{[^}]*(ViewTransition|addTransitionType)[^}]*\}\s*from\s*['"]react['"]/;
if (importRegex.test(code)) {
const result = code.replace(
/from\s*['"]react['"]/g,
'from "virtual:react-with-canary"',
);
if (result !== code) {
return { code: result, map: null };
}
}
}
return null;
},
},
// MDX support with CodeHike remark/recma plugins โ transforms .mdx files
// into React components and processes !!col directives, code annotations, etc.
mdx({
remarkPlugins: [[remarkCodeHike, codeHikeConfig]],
recmaPlugins: [[recmaCodeHike, codeHikeConfig]],
}),
// vinext plugin (provides all next/* shims, routing, SSR, RSC).
// @vitejs/plugin-rsc is auto-registered when app/ is detected.
vinext(),
// Cloudflare Workers plugin โ builds for workerd runtime.
// The worker entry runs in the RSC environment, with SSR as a child.
cloudflare({
viteEnvironment: {
name: "rsc",
childEnvironments: ["ssr"],
},
}),
],
resolve: {
alias: {
// Map #/* imports to the project root (matches tsconfig paths)
"#": path.resolve(__dirname),
// Map bare 'app/' imports (tsconfig baseUrl: ".")
"app": path.resolve(__dirname, "app"),
// server-only is a guard package โ resolve to empty module in SSR
"server-only": path.resolve(__dirname, "server-only-shim.ts"),
},
},
// Use postcss.config.js for Tailwind CSS processing
// (Do NOT override with empty plugins array)
});