-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvite.config.ts
More file actions
132 lines (128 loc) Β· 4.76 KB
/
vite.config.ts
File metadata and controls
132 lines (128 loc) Β· 4.76 KB
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import { mkdirSync } from "node:fs";
import { resolve } from "node:path";
import { cloudflare } from "@cloudflare/vite-plugin";
import { visualizer } from "rollup-plugin-visualizer";
import vinext from "vinext";
import { defineConfig, type Plugin } from "vite";
/**
* Vite plugin that stubs server-only modules for client environments.
* - `cloudflare:workers` β used by lib/referral.ts etc., doesn't exist in browser
* - `node:async_hooks` β vinext's headers.js shim imports AsyncLocalStorage,
* which Vite externalizes to a browser stub that lacks the named export
*/
function clientModuleStubs(): Plugin {
const stubs: Record<string, string> = {
"cloudflare:workers": resolve("lib/stubs/cloudflare-workers-client-stub.mjs"),
"node:async_hooks": resolve("lib/stubs/async-hooks-client-stub.mjs"),
async_hooks: resolve("lib/stubs/async-hooks-client-stub.mjs"),
};
return {
name: "client-module-stubs",
enforce: "pre",
resolveId(id) {
if (this.environment?.name === "client" && stubs[id]) {
return stubs[id];
}
return null;
},
};
}
/**
* Vite plugin that extends vinext's client manualChunks to split heavy vendor
* deps into their own chunks β keeps the main client bundle under 500 KB.
*
* vinext intentionally only splits React/scheduler into "framework" and its own
* shims into "vinext", leaving all other vendor code to Rollup's default splitting.
* This works well for most apps, but ours pulls radix-ui, react-hook-form, and
* better-auth into a single mega-chunk. We wrap vinext's function to split those
* specific packages out while preserving all other vinext chunking decisions.
*/
function clientVendorSplit(): Plugin {
return {
name: "client-vendor-split",
configResolved(config) {
const clientEnv = config.environments?.client;
const output = clientEnv?.build?.rollupOptions?.output;
if (output && typeof output === "object" && !Array.isArray(output)) {
const original = output.manualChunks;
output.manualChunks = (id: string, api: unknown) => {
// Split heavy vendor deps that bloat the client mega-chunk
if (id.includes("node_modules/@radix-ui")) return "vendor-radix";
if (id.includes("node_modules/react-hook-form")) return "vendor-forms";
if (id.includes("node_modules/better-auth")) return "vendor-auth";
// Delegate to vinext's default chunking
if (typeof original === "function") {
return (original as (id: string, api: unknown) => string | undefined)(id, api);
}
return undefined;
};
}
},
};
}
/**
* Workaround for vinext cloudflare-build bug: the plugin writes
* dist/client/_headers via writeFileSync without creating the directory first.
* This plugin ensures the output directory exists before writeBundle hooks fire.
* Remove once vinext fixes this upstream.
*/
function ensureClientDir(): Plugin {
return {
name: "ensure-client-dir",
enforce: "pre",
writeBundle: {
order: "pre",
handler() {
if (this.environment?.name === "client") {
mkdirSync("dist/client", { recursive: true });
}
},
},
};
}
export default defineConfig({
plugins: [
ensureClientDir(),
vinext(),
cloudflare({
viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] },
}),
clientModuleStubs(),
clientVendorSplit(),
],
resolve: {
alias: {
// Bundle stubs β replaces wrangler.jsonc alias block
// @vercel/og β doesn't work on CF Workers, vinext bundles it anyway (~2MB)
"next/dist/compiled/@vercel/og/index.edge.js": resolve("lib/stubs/og-stub.js"),
// @zxcvbn-ts β password dictionaries (1.73MB), only client-side
"@zxcvbn-ts/core": resolve("lib/stubs/zxcvbn-core-stub.mjs"),
"@zxcvbn-ts/language-common": resolve("lib/stubs/zxcvbn-lang-stub.mjs"),
"@zxcvbn-ts/language-en": resolve("lib/stubs/zxcvbn-lang-stub.mjs"),
// zod/v3 β required bundle shim; only the runtime v3 conversion path is dead
"zod/v3": resolve("lib/stubs/zod-v3-stub.mjs"),
},
},
build: {
rollupOptions: {
plugins: [
// Bundle visualizer β only runs when ANALYZE=true
...(process.env.ANALYZE === "true"
? [visualizer({ open: true, gzipSize: true, filename: "dist/stats.html" })]
: []),
],
onwarn(warning, warn) {
// vinext virtual entry imports "middleware" from proxy.ts even though
// only "proxy" / "default" are used β suppress the harmless warning
if (
warning.code === "MISSING_EXPORT" &&
warning.message?.includes('"middleware"') &&
warning.message?.includes("proxy.ts")
) {
return;
}
warn(warning);
},
},
},
});