@lovable.dev/vite-tanstack-config 2.7.5-rc.3 → 2.7.5-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,481 +1,569 @@
1
- import { loadEnv, mergeConfig } from "vite";
2
- import https from "node:https";
3
- //#region src/assetsProxyPlugin.ts
4
- const ASSET_RE = /^\/__l5e\/assets-v1\//;
5
- const PREVIEW_HOST_ENV = "LOVABLE_PREVIEW_HOST";
6
- const HOP_BY_HOP = /* @__PURE__ */ new Set([
7
- "host",
8
- "connection",
9
- "keep-alive",
10
- "proxy-authenticate",
11
- "proxy-authorization",
12
- "te",
13
- "trailer",
14
- "transfer-encoding",
15
- "upgrade",
16
- "content-length",
17
- "cookie"
1
+ // src/index.ts
2
+ import { mergeConfig, loadEnv } from "vite";
3
+
4
+ // src/assetsProxyPlugin.ts
5
+ import https from "https";
6
+ var ASSET_RE = /^\/__l5e\/assets-v1\//;
7
+ var PREVIEW_HOST_ENV = "LOVABLE_PREVIEW_HOST";
8
+ var HOP_BY_HOP = /* @__PURE__ */ new Set([
9
+ "host",
10
+ "connection",
11
+ "keep-alive",
12
+ "proxy-authenticate",
13
+ "proxy-authorization",
14
+ "te",
15
+ "trailer",
16
+ "transfer-encoding",
17
+ "upgrade",
18
+ "content-length",
19
+ "cookie"
18
20
  ]);
19
21
  function pickRequestHeaders(src) {
20
- const out = {};
21
- for (const [k, v] of Object.entries(src)) {
22
- if (v === void 0) continue;
23
- if (HOP_BY_HOP.has(k.toLowerCase())) continue;
24
- out[k] = v;
25
- }
26
- return out;
22
+ const out = {};
23
+ for (const [k, v] of Object.entries(src)) {
24
+ if (v === void 0)
25
+ continue;
26
+ if (HOP_BY_HOP.has(k.toLowerCase()))
27
+ continue;
28
+ out[k] = v;
29
+ }
30
+ return out;
27
31
  }
28
- /**
29
- * Forwards `/__l5e/assets-v1/*` requests from the Vite dev server to the
30
- * Lovable preview proxy so assets resolve when the page is loaded directly
31
- * from the sandbox dev server (e.g. `localhost:8080`) instead of via
32
- * `*.lovable.app` / `*.lovableproject.com`.
33
- *
34
- * No-op unless `LOVABLE_PREVIEW_HOST` is set (host only, no scheme), e.g.
35
- * `id-preview--<project-id>.lovable.app`.
36
- */
37
32
  function lovableAssetsProxyPlugin() {
38
- const previewHost = process.env[PREVIEW_HOST_ENV]?.trim();
39
- return {
40
- name: "lovable:assets-proxy",
41
- apply: "serve",
42
- configureServer(server) {
43
- if (!previewHost) return;
44
- const middleware = (req, res, next) => {
45
- const url = req.url ?? "";
46
- if (!ASSET_RE.test(url)) return next();
47
- const upstream = https.request({
48
- host: previewHost,
49
- port: 443,
50
- method: req.method,
51
- path: url,
52
- headers: {
53
- ...pickRequestHeaders(req.headers),
54
- host: previewHost
55
- }
56
- }, (upRes) => {
57
- res.statusCode = upRes.statusCode ?? 502;
58
- for (const [k, v] of Object.entries(upRes.headers)) {
59
- if (v === void 0) continue;
60
- if (HOP_BY_HOP.has(k.toLowerCase())) continue;
61
- res.setHeader(k, v);
62
- }
63
- upRes.pipe(res);
64
- });
65
- upstream.on("error", (err) => {
66
- server.config.logger.warn(`[@lovable.dev/vite-tanstack-config] asset proxy ${url} → ${previewHost} failed: ${err.message}`);
67
- if (!res.headersSent) {
68
- res.statusCode = 502;
69
- res.end();
70
- } else res.destroy(err);
71
- });
72
- req.pipe(upstream);
73
- };
74
- server.middlewares.use(middleware);
75
- }
76
- };
33
+ const previewHost = process.env[PREVIEW_HOST_ENV]?.trim();
34
+ return {
35
+ name: "lovable:assets-proxy",
36
+ apply: "serve",
37
+ configureServer(server) {
38
+ if (!previewHost)
39
+ return;
40
+ const middleware = (req, res, next) => {
41
+ const url = req.url ?? "";
42
+ if (!ASSET_RE.test(url))
43
+ return next();
44
+ const upstream = https.request(
45
+ {
46
+ host: previewHost,
47
+ port: 443,
48
+ method: req.method,
49
+ path: url,
50
+ headers: { ...pickRequestHeaders(req.headers), host: previewHost }
51
+ },
52
+ (upRes) => {
53
+ res.statusCode = upRes.statusCode ?? 502;
54
+ for (const [k, v] of Object.entries(upRes.headers)) {
55
+ if (v === void 0)
56
+ continue;
57
+ if (HOP_BY_HOP.has(k.toLowerCase()))
58
+ continue;
59
+ res.setHeader(k, v);
60
+ }
61
+ upRes.pipe(res);
62
+ }
63
+ );
64
+ upstream.on("error", (err) => {
65
+ server.config.logger.warn(
66
+ `[@lovable.dev/vite-tanstack-config] asset proxy ${url} \u2192 ${previewHost} failed: ${err.message}`
67
+ );
68
+ if (!res.headersSent) {
69
+ res.statusCode = 502;
70
+ res.end();
71
+ } else {
72
+ res.destroy(err);
73
+ }
74
+ });
75
+ req.pipe(upstream);
76
+ };
77
+ server.middlewares.use(middleware);
78
+ }
79
+ };
77
80
  }
78
- //#endregion
79
- //#region src/index.ts
80
- const SANDBOX_ENV_VAR = "DEV_SERVER__PROJECT_PATH";
81
- const LOVABLE_SANDBOX_ENV_VAR = "LOVABLE_SANDBOX";
81
+
82
+ // src/index.ts
83
+ var SANDBOX_ENV_VAR = "DEV_SERVER__PROJECT_PATH";
84
+ var LOVABLE_SANDBOX_ENV_VAR = "LOVABLE_SANDBOX";
82
85
  function isSandboxEnvironment() {
83
- return process.env[LOVABLE_SANDBOX_ENV_VAR] === "1" || !!process.env[SANDBOX_ENV_VAR];
86
+ return process.env[LOVABLE_SANDBOX_ENV_VAR] === "1" || !!process.env[SANDBOX_ENV_VAR];
84
87
  }
85
- const TOOL_MANAGED_WATCH_IGNORES = [
86
- "**/.workspace/**",
87
- "**/.agents/**",
88
- "**/.claude/**",
89
- "**/.lovable/**",
90
- "**/.tanstack/tmp/**"
88
+ var TOOL_MANAGED_WATCH_IGNORES = [
89
+ "**/.workspace/**",
90
+ "**/.agents/**",
91
+ "**/.claude/**",
92
+ "**/.lovable/**",
93
+ "**/.tanstack/tmp/**"
91
94
  ];
92
95
  function logWarning(message) {
93
- console.warn(`[@lovable.dev/vite-tanstack-config] ${message}`);
96
+ console.warn(`[@lovable.dev/vite-tanstack-config] ${message}`);
94
97
  }
95
- const NITRO_DEFAULT_PRESET_MIN = [
96
- 3,
97
- 0,
98
- 260603
99
- ];
98
+ var NITRO_DEFAULT_PRESET_MIN = [3, 0, 260603];
100
99
  async function nitroSupportsDefaultPreset() {
101
- try {
102
- const { createRequire } = await import("node:module");
103
- const { version } = createRequire(`${process.cwd()}/package.json`)("nitro/package.json");
104
- const match = typeof version === "string" ? /^(\d+)\.(\d+)\.(\d+)/.exec(version) : null;
105
- if (!match) return true;
106
- const parts = [
107
- Number(match[1]),
108
- Number(match[2]),
109
- Number(match[3])
110
- ];
111
- for (let i = 0; i < 3; i++) if (parts[i] !== NITRO_DEFAULT_PRESET_MIN[i]) return parts[i] > NITRO_DEFAULT_PRESET_MIN[i];
112
- return true;
113
- } catch {
114
- return true;
115
- }
100
+ try {
101
+ const { createRequire } = await import("module");
102
+ const require2 = createRequire(`${process.cwd()}/package.json`);
103
+ const { version } = require2("nitro/package.json");
104
+ const match = typeof version === "string" ? /^(\d+)\.(\d+)\.(\d+)/.exec(version) : null;
105
+ if (!match)
106
+ return true;
107
+ const parts = [Number(match[1]), Number(match[2]), Number(match[3])];
108
+ for (let i = 0; i < 3; i++) {
109
+ if (parts[i] !== NITRO_DEFAULT_PRESET_MIN[i])
110
+ return parts[i] > NITRO_DEFAULT_PRESET_MIN[i];
111
+ }
112
+ return true;
113
+ } catch {
114
+ return true;
115
+ }
116
116
  }
117
117
  function validateHeaders(config) {
118
- const headers = config.server?.headers;
119
- if (!headers) return;
120
- const headerKeys = Object.keys(headers);
121
- if (headerKeys.length > 0) logWarning(`Your config includes server headers that will be removed in sandbox mode: ${headerKeys.join(", ")}`);
118
+ const headers = config.server?.headers;
119
+ if (!headers)
120
+ return;
121
+ const headerKeys = Object.keys(headers);
122
+ if (headerKeys.length > 0) {
123
+ logWarning(`Your config includes server headers that will be removed in sandbox mode: ${headerKeys.join(", ")}`);
124
+ }
122
125
  }
123
126
  function isExternalUrl(url) {
124
- try {
125
- const parsed = new URL(url);
126
- return ![
127
- "localhost",
128
- "127.0.0.1",
129
- "::1",
130
- "0.0.0.0",
131
- "::"
132
- ].includes(parsed.hostname);
133
- } catch {
134
- return false;
135
- }
127
+ try {
128
+ const parsed = new URL(url);
129
+ return !["localhost", "127.0.0.1", "::1", "0.0.0.0", "::"].includes(parsed.hostname);
130
+ } catch {
131
+ return false;
132
+ }
136
133
  }
137
134
  function validateProxy(config) {
138
- const proxy = config.server?.proxy;
139
- if (!proxy) return;
140
- for (const [path, proxyConfig] of Object.entries(proxy)) if (typeof proxyConfig === "string") {
141
- if (isExternalUrl(proxyConfig)) logWarning(`Proxy '${path}' targets external URL: ${proxyConfig}. This may cause issues in the sandbox environment.`);
142
- } else if (proxyConfig) {
143
- const target = typeof proxyConfig.target === "string" ? proxyConfig.target : void 0;
144
- if (target && isExternalUrl(target)) logWarning(`Proxy '${path}' targets external URL: ${target}. This may cause issues in the sandbox environment.`);
145
- if (proxyConfig.ws) logWarning(`Proxy '${path}' enables WebSocket proxying, which may interfere with HMR.`);
146
- if (proxyConfig.changeOrigin) logWarning(`Proxy '${path}' changes origin, which may cause CORS issues.`);
147
- }
135
+ const proxy = config.server?.proxy;
136
+ if (!proxy)
137
+ return;
138
+ for (const [path, proxyConfig] of Object.entries(proxy)) {
139
+ if (typeof proxyConfig === "string") {
140
+ if (isExternalUrl(proxyConfig)) {
141
+ logWarning(
142
+ `Proxy '${path}' targets external URL: ${proxyConfig}. This may cause issues in the sandbox environment.`
143
+ );
144
+ }
145
+ } else if (proxyConfig) {
146
+ const target = typeof proxyConfig.target === "string" ? proxyConfig.target : void 0;
147
+ if (target && isExternalUrl(target)) {
148
+ logWarning(
149
+ `Proxy '${path}' targets external URL: ${target}. This may cause issues in the sandbox environment.`
150
+ );
151
+ }
152
+ if (proxyConfig.ws) {
153
+ logWarning(`Proxy '${path}' enables WebSocket proxying, which may interfere with HMR.`);
154
+ }
155
+ if (proxyConfig.changeOrigin) {
156
+ logWarning(`Proxy '${path}' changes origin, which may cause CORS issues.`);
157
+ }
158
+ }
159
+ }
148
160
  }
149
161
  function validateCors(config) {
150
- if (config.server?.cors && typeof config.server.cors !== "boolean" && config.server.cors !== null) logWarning("Custom CORS config detected. Sandbox requires specific CORS settings for proxy compatibility.");
162
+ if (config.server?.cors && typeof config.server.cors !== "boolean" && config.server.cors !== null) {
163
+ logWarning("Custom CORS config detected. Sandbox requires specific CORS settings for proxy compatibility.");
164
+ }
151
165
  }
152
166
  function validatePort(config) {
153
- if (config.server?.port && config.server.port !== 8080) logWarning(`Port ${config.server.port} configured, but sandbox requires port 8080. Using 8080.`);
167
+ if (config.server?.port && config.server.port !== 8080) {
168
+ logWarning(`Port ${config.server.port} configured, but sandbox requires port 8080. Using 8080.`);
169
+ }
154
170
  }
155
171
  function validateConfig(config) {
156
- validateHeaders(config);
157
- validateProxy(config);
158
- validateCors(config);
159
- validatePort(config);
172
+ validateHeaders(config);
173
+ validateProxy(config);
174
+ validateCors(config);
175
+ validatePort(config);
160
176
  }
161
177
  function cleanServerConfig(config) {
162
- if (config.server?.headers || config.server?.cors || config.server?.proxy) {
163
- const { headers: _headers, cors: _cors, proxy: _proxy, ...serverRest } = config.server;
164
- return {
165
- ...config,
166
- server: serverRest
167
- };
168
- }
169
- return config;
178
+ if (config.server?.headers || config.server?.cors || config.server?.proxy) {
179
+ const { headers: _headers, cors: _cors, proxy: _proxy, ...serverRest } = config.server;
180
+ return {
181
+ ...config,
182
+ server: serverRest
183
+ };
184
+ }
185
+ return config;
170
186
  }
171
187
  function applyWatchDebounceDefaults(config) {
172
- const existingWatch = config.server?.watch ?? {};
173
- const existingAwaitWriteFinish = existingWatch.awaitWriteFinish;
174
- const hasAwaitWriteFinishObject = !!existingAwaitWriteFinish && typeof existingAwaitWriteFinish === "object" && !Array.isArray(existingAwaitWriteFinish);
175
- return mergeConfig(config, { server: { watch: {
176
- ...existingWatch,
177
- awaitWriteFinish: {
178
- ...hasAwaitWriteFinishObject ? existingAwaitWriteFinish : {},
179
- stabilityThreshold: 1e3,
180
- pollInterval: 100
181
- }
182
- } } });
188
+ const existingWatch = config.server?.watch ?? {};
189
+ const existingAwaitWriteFinish = existingWatch.awaitWriteFinish;
190
+ const hasAwaitWriteFinishObject = !!existingAwaitWriteFinish && typeof existingAwaitWriteFinish === "object" && !Array.isArray(existingAwaitWriteFinish);
191
+ return mergeConfig(config, {
192
+ server: {
193
+ watch: {
194
+ ...existingWatch,
195
+ awaitWriteFinish: {
196
+ ...hasAwaitWriteFinishObject ? existingAwaitWriteFinish : {},
197
+ stabilityThreshold: 1e3,
198
+ pollInterval: 100
199
+ }
200
+ }
201
+ }
202
+ });
183
203
  }
184
- const SSR_CAPTURE_KEY = "__LOVABLE_TANSTACK_CAPTURE_SSR_ERROR__";
185
- const HANDLER_RAN_KEY = "__LOVABLE_TANSTACK_HANDLER_RAN__";
186
- const BUILD_ERROR_MESSAGE = /transform failed with \d+ error|failed to parse source/i;
204
+ var SSR_CAPTURE_KEY = "__LOVABLE_TANSTACK_CAPTURE_SSR_ERROR__";
205
+ var HANDLER_RAN_KEY = "__LOVABLE_TANSTACK_HANDLER_RAN__";
206
+ var BUILD_ERROR_MESSAGE = /transform failed with \d+ error|failed to parse source/i;
187
207
  function isBuildError(error) {
188
- if (typeof error === "string") return BUILD_ERROR_MESSAGE.test(error);
189
- if (!error || typeof error !== "object") return false;
190
- const e = error;
191
- if (typeof e.plugin === "string" && (e.plugin.startsWith("vite:") || e.plugin.startsWith("esbuild:"))) return true;
192
- if (e.frame != null && e.loc != null) return true;
193
- if (Array.isArray(e.errors) && e.errors.length > 0 && e.errors.every((x) => x != null && typeof x === "object" && "text" in x && "location" in x)) return true;
194
- const message = typeof e.message === "string" ? e.message : "";
195
- return BUILD_ERROR_MESSAGE.test(message);
208
+ if (typeof error === "string")
209
+ return BUILD_ERROR_MESSAGE.test(error);
210
+ if (!error || typeof error !== "object")
211
+ return false;
212
+ const e = error;
213
+ if (typeof e.plugin === "string" && (e.plugin.startsWith("vite:") || e.plugin.startsWith("esbuild:")))
214
+ return true;
215
+ if (e.frame != null && e.loc != null)
216
+ return true;
217
+ if (Array.isArray(e.errors) && e.errors.length > 0 && e.errors.every((x) => x != null && typeof x === "object" && "text" in x && "location" in x)) {
218
+ return true;
219
+ }
220
+ const message = typeof e.message === "string" ? e.message : "";
221
+ return BUILD_ERROR_MESSAGE.test(message);
196
222
  }
197
- const BUILD_DIAGNOSTICS_PLUGIN_NAME = "lovable-build-error-diagnostics";
198
- const MAX_DIAGNOSTIC_JSON_LENGTH = 6e3;
199
- const MAX_DIAGNOSTIC_STRING_LENGTH = 2e3;
200
- const MAX_DIAGNOSTIC_NESTED_ERRORS = 20;
201
- const MAX_DIAGNOSTIC_DEPTH = 4;
202
- const COMPACT_DIAGNOSTIC_STRING_LENGTH = 300;
203
- const COMPACT_DIAGNOSTIC_NESTED_ERRORS = 3;
204
- const COMPACT_DIAGNOSTIC_DEPTH = 3;
205
- const DIAGNOSTIC_ERROR_FIELDS = [
206
- "name",
207
- "message",
208
- "plugin",
209
- "pluginCode",
210
- "id",
211
- "moduleId",
212
- "hook",
213
- "code",
214
- "loc",
215
- "frame",
216
- "stack",
217
- "cause"
223
+ var BUILD_DIAGNOSTICS_PLUGIN_NAME = "lovable-build-error-diagnostics";
224
+ var MAX_DIAGNOSTIC_JSON_LENGTH = 6e3;
225
+ var MAX_DIAGNOSTIC_STRING_LENGTH = 2e3;
226
+ var MAX_DIAGNOSTIC_NESTED_ERRORS = 20;
227
+ var MAX_DIAGNOSTIC_DEPTH = 4;
228
+ var COMPACT_DIAGNOSTIC_STRING_LENGTH = 300;
229
+ var COMPACT_DIAGNOSTIC_NESTED_ERRORS = 3;
230
+ var COMPACT_DIAGNOSTIC_DEPTH = 3;
231
+ var DIAGNOSTIC_ERROR_FIELDS = [
232
+ "name",
233
+ "message",
234
+ "plugin",
235
+ "pluginCode",
236
+ "id",
237
+ "moduleId",
238
+ "hook",
239
+ "code",
240
+ "loc",
241
+ "frame",
242
+ "stack",
243
+ "cause"
218
244
  ];
219
245
  function truncateDiagnosticString(value) {
220
- return truncateDiagnosticStringTo(value, MAX_DIAGNOSTIC_STRING_LENGTH);
246
+ return truncateDiagnosticStringTo(value, MAX_DIAGNOSTIC_STRING_LENGTH);
221
247
  }
222
248
  function truncateDiagnosticStringTo(value, maxLength) {
223
- return value.length > maxLength ? `${value.slice(0, maxLength)}...[truncated]` : value;
249
+ return value.length > maxLength ? `${value.slice(0, maxLength)}...[truncated]` : value;
224
250
  }
225
251
  function readDiagnosticProperty(value, key) {
226
- try {
227
- return value[key];
228
- } catch (error) {
229
- return `[unreadable: ${error instanceof Error ? error.message : String(error)}]`;
230
- }
252
+ try {
253
+ return value[key];
254
+ } catch (error) {
255
+ return `[unreadable: ${error instanceof Error ? error.message : String(error)}]`;
256
+ }
231
257
  }
232
258
  function serializeOwnDiagnosticProperties(value, depth, seen) {
233
- const keys = Object.keys(value);
234
- const output = {};
235
- for (const key of keys.slice(0, MAX_DIAGNOSTIC_NESTED_ERRORS)) {
236
- const nested = readDiagnosticProperty(value, key);
237
- if (nested == null || nested === "") continue;
238
- output[key] = serializeDiagnosticValue(nested, depth + 1, seen);
239
- }
240
- if (keys.length > MAX_DIAGNOSTIC_NESTED_ERRORS) output.properties_truncated = keys.length - MAX_DIAGNOSTIC_NESTED_ERRORS;
241
- return output;
259
+ const keys = Object.keys(value);
260
+ const output = {};
261
+ for (const key of keys.slice(0, MAX_DIAGNOSTIC_NESTED_ERRORS)) {
262
+ const nested = readDiagnosticProperty(value, key);
263
+ if (nested == null || nested === "")
264
+ continue;
265
+ output[key] = serializeDiagnosticValue(nested, depth + 1, seen);
266
+ }
267
+ if (keys.length > MAX_DIAGNOSTIC_NESTED_ERRORS) {
268
+ output.properties_truncated = keys.length - MAX_DIAGNOSTIC_NESTED_ERRORS;
269
+ }
270
+ return output;
242
271
  }
243
272
  function describeDiagnosticPrimitive(value) {
244
- if (typeof value === "symbol") return value.description ? `Symbol(${value.description})` : "Symbol()";
245
- if (typeof value === "bigint") return value.toString();
246
- if (typeof value === "function") return `[function ${value.name || "anonymous"}]`;
247
- return String(value);
273
+ if (typeof value === "symbol")
274
+ return value.description ? `Symbol(${value.description})` : "Symbol()";
275
+ if (typeof value === "bigint")
276
+ return value.toString();
277
+ if (typeof value === "function")
278
+ return `[function ${value.name || "anonymous"}]`;
279
+ return String(value);
248
280
  }
249
281
  function describeDiagnosticObject(value) {
250
- const constructorName = value.constructor?.name;
251
- if (typeof constructorName === "string" && constructorName !== "" && constructorName !== "Object") return `[${constructorName}]`;
252
- return Object.prototype.toString.call(value);
282
+ const constructorName = value.constructor?.name;
283
+ if (typeof constructorName === "string" && constructorName !== "" && constructorName !== "Object") {
284
+ return `[${constructorName}]`;
285
+ }
286
+ return Object.prototype.toString.call(value);
253
287
  }
254
288
  function serializeDiagnosticValue(value, depth, seen) {
255
- if (value == null) return value;
256
- if (typeof value === "string") return truncateDiagnosticString(value);
257
- if (typeof value === "number" || typeof value === "boolean") return value;
258
- if (typeof value !== "object") return describeDiagnosticPrimitive(value);
259
- if (seen.has(value)) return "[circular]";
260
- if (depth >= MAX_DIAGNOSTIC_DEPTH) return "[max depth]";
261
- seen.add(value);
262
- try {
263
- if (Array.isArray(value)) return value.slice(0, MAX_DIAGNOSTIC_NESTED_ERRORS).map((item) => serializeDiagnosticValue(item, depth + 1, seen));
264
- const record = value;
265
- const output = {};
266
- for (const field of DIAGNOSTIC_ERROR_FIELDS) {
267
- if (!(field in record)) continue;
268
- const nested = readDiagnosticProperty(record, field);
269
- if (nested == null || nested === "") continue;
270
- output[field] = serializeDiagnosticValue(nested, depth + 1, seen);
271
- }
272
- const nestedErrors = readDiagnosticProperty(record, "errors");
273
- if (Array.isArray(nestedErrors)) {
274
- output.errors = nestedErrors.slice(0, MAX_DIAGNOSTIC_NESTED_ERRORS).map((item) => serializeDiagnosticValue(item, depth + 1, seen));
275
- if (nestedErrors.length > MAX_DIAGNOSTIC_NESTED_ERRORS) output.errors_truncated = nestedErrors.length - MAX_DIAGNOSTIC_NESTED_ERRORS;
276
- } else if (nestedErrors != null && nestedErrors !== "") output.errors = serializeDiagnosticValue(nestedErrors, depth + 1, seen);
277
- if (Object.keys(output).length > 0) return output;
278
- const ownProperties = serializeOwnDiagnosticProperties(record, depth, seen);
279
- return Object.keys(ownProperties).length > 0 ? ownProperties : describeDiagnosticObject(value);
280
- } catch (error) {
281
- return `[unreadable object: ${error instanceof Error ? error.message : String(error)}]`;
282
- } finally {
283
- seen.delete(value);
284
- }
289
+ if (value == null)
290
+ return value;
291
+ if (typeof value === "string")
292
+ return truncateDiagnosticString(value);
293
+ if (typeof value === "number" || typeof value === "boolean")
294
+ return value;
295
+ if (typeof value !== "object")
296
+ return describeDiagnosticPrimitive(value);
297
+ if (seen.has(value))
298
+ return "[circular]";
299
+ if (depth >= MAX_DIAGNOSTIC_DEPTH)
300
+ return "[max depth]";
301
+ seen.add(value);
302
+ try {
303
+ if (Array.isArray(value)) {
304
+ return value.slice(0, MAX_DIAGNOSTIC_NESTED_ERRORS).map((item) => serializeDiagnosticValue(item, depth + 1, seen));
305
+ }
306
+ const record = value;
307
+ const output = {};
308
+ for (const field of DIAGNOSTIC_ERROR_FIELDS) {
309
+ if (!(field in record))
310
+ continue;
311
+ const nested = readDiagnosticProperty(record, field);
312
+ if (nested == null || nested === "")
313
+ continue;
314
+ output[field] = serializeDiagnosticValue(nested, depth + 1, seen);
315
+ }
316
+ const nestedErrors = readDiagnosticProperty(record, "errors");
317
+ if (Array.isArray(nestedErrors)) {
318
+ output.errors = nestedErrors.slice(0, MAX_DIAGNOSTIC_NESTED_ERRORS).map((item) => serializeDiagnosticValue(item, depth + 1, seen));
319
+ if (nestedErrors.length > MAX_DIAGNOSTIC_NESTED_ERRORS) {
320
+ output.errors_truncated = nestedErrors.length - MAX_DIAGNOSTIC_NESTED_ERRORS;
321
+ }
322
+ } else if (nestedErrors != null && nestedErrors !== "") {
323
+ output.errors = serializeDiagnosticValue(nestedErrors, depth + 1, seen);
324
+ }
325
+ if (Object.keys(output).length > 0)
326
+ return output;
327
+ const ownProperties = serializeOwnDiagnosticProperties(record, depth, seen);
328
+ return Object.keys(ownProperties).length > 0 ? ownProperties : describeDiagnosticObject(value);
329
+ } catch (error) {
330
+ return `[unreadable object: ${error instanceof Error ? error.message : String(error)}]`;
331
+ } finally {
332
+ seen.delete(value);
333
+ }
285
334
  }
286
335
  function compactDiagnosticValue(value, depth) {
287
- if (value == null) return value;
288
- if (typeof value === "string") return truncateDiagnosticStringTo(value, COMPACT_DIAGNOSTIC_STRING_LENGTH);
289
- if (typeof value === "number" || typeof value === "boolean") return value;
290
- if (typeof value !== "object") return describeDiagnosticPrimitive(value);
291
- if (depth >= COMPACT_DIAGNOSTIC_DEPTH) return "[max depth]";
292
- try {
293
- if (Array.isArray(value)) {
294
- const items = value.slice(0, COMPACT_DIAGNOSTIC_NESTED_ERRORS).map((item) => compactDiagnosticValue(item, depth + 1));
295
- if (value.length > COMPACT_DIAGNOSTIC_NESTED_ERRORS) items.push(`[${value.length - COMPACT_DIAGNOSTIC_NESTED_ERRORS} items truncated]`);
296
- return items;
297
- }
298
- const record = value;
299
- const output = {};
300
- const keys = Object.keys(record);
301
- for (const key of keys.slice(0, MAX_DIAGNOSTIC_NESTED_ERRORS)) {
302
- const nested = readDiagnosticProperty(record, key);
303
- if (nested == null || nested === "") continue;
304
- output[key] = compactDiagnosticValue(nested, depth + 1);
305
- }
306
- if (keys.length > MAX_DIAGNOSTIC_NESTED_ERRORS) output.properties_truncated = keys.length - MAX_DIAGNOSTIC_NESTED_ERRORS;
307
- return output;
308
- } catch (error) {
309
- return `[unreadable object: ${error instanceof Error ? error.message : String(error)}]`;
310
- }
336
+ if (value == null)
337
+ return value;
338
+ if (typeof value === "string")
339
+ return truncateDiagnosticStringTo(value, COMPACT_DIAGNOSTIC_STRING_LENGTH);
340
+ if (typeof value === "number" || typeof value === "boolean")
341
+ return value;
342
+ if (typeof value !== "object")
343
+ return describeDiagnosticPrimitive(value);
344
+ if (depth >= COMPACT_DIAGNOSTIC_DEPTH)
345
+ return "[max depth]";
346
+ try {
347
+ if (Array.isArray(value)) {
348
+ const items = value.slice(0, COMPACT_DIAGNOSTIC_NESTED_ERRORS).map((item) => compactDiagnosticValue(item, depth + 1));
349
+ if (value.length > COMPACT_DIAGNOSTIC_NESTED_ERRORS) {
350
+ items.push(`[${value.length - COMPACT_DIAGNOSTIC_NESTED_ERRORS} items truncated]`);
351
+ }
352
+ return items;
353
+ }
354
+ const record = value;
355
+ const output = {};
356
+ const keys = Object.keys(record);
357
+ for (const key of keys.slice(0, MAX_DIAGNOSTIC_NESTED_ERRORS)) {
358
+ const nested = readDiagnosticProperty(record, key);
359
+ if (nested == null || nested === "")
360
+ continue;
361
+ output[key] = compactDiagnosticValue(nested, depth + 1);
362
+ }
363
+ if (keys.length > MAX_DIAGNOSTIC_NESTED_ERRORS) {
364
+ output.properties_truncated = keys.length - MAX_DIAGNOSTIC_NESTED_ERRORS;
365
+ }
366
+ return output;
367
+ } catch (error) {
368
+ return `[unreadable object: ${error instanceof Error ? error.message : String(error)}]`;
369
+ }
311
370
  }
312
371
  function stringifyDiagnosticPayload(diagnostics) {
313
- const full = JSON.stringify(diagnostics);
314
- if (full && full.length <= MAX_DIAGNOSTIC_JSON_LENGTH) return full;
315
- const compact = compactDiagnosticValue(diagnostics, 0);
316
- const compactPayload = compact != null && typeof compact === "object" && !Array.isArray(compact) ? {
317
- ...compact,
318
- diagnostics_truncated: true
319
- } : {
320
- diagnostics_truncated: true,
321
- value: compact
322
- };
323
- const compactJson = JSON.stringify(compactPayload);
324
- if (compactJson.length <= MAX_DIAGNOSTIC_JSON_LENGTH) return compactJson;
325
- return JSON.stringify({
326
- diagnostics_truncated: true,
327
- message: "Diagnostic payload exceeded output budget."
328
- });
372
+ const full = JSON.stringify(diagnostics);
373
+ if (full && full.length <= MAX_DIAGNOSTIC_JSON_LENGTH)
374
+ return full;
375
+ const compact = compactDiagnosticValue(diagnostics, 0);
376
+ const compactPayload = compact != null && typeof compact === "object" && !Array.isArray(compact) ? { ...compact, diagnostics_truncated: true } : { diagnostics_truncated: true, value: compact };
377
+ const compactJson = JSON.stringify(compactPayload);
378
+ if (compactJson.length <= MAX_DIAGNOSTIC_JSON_LENGTH)
379
+ return compactJson;
380
+ return JSON.stringify({
381
+ diagnostics_truncated: true,
382
+ message: "Diagnostic payload exceeded output budget."
383
+ });
329
384
  }
330
385
  function lovableBuildErrorDiagnostics() {
331
- let emitted = false;
332
- const emitDiagnostics = (error) => {
333
- if (!error || emitted) return;
334
- emitted = true;
335
- try {
336
- const output = `\n[lovable-build-error-diagnostics]\n${stringifyDiagnosticPayload(serializeDiagnosticValue(error, 0, /* @__PURE__ */ new WeakSet()))}\n[/lovable-build-error-diagnostics]\n`;
337
- let written = false;
338
- const write = () => {
339
- if (written) return;
340
- written = true;
341
- try {
342
- process.stderr.write(output);
343
- } catch {}
344
- };
345
- setImmediate(write);
346
- process.once("exit", write);
347
- } catch {}
348
- };
349
- return {
350
- name: BUILD_DIAGNOSTICS_PLUGIN_NAME,
351
- apply: "build",
352
- buildEnd(error) {
353
- emitDiagnostics(error);
354
- },
355
- renderError(error) {
356
- emitDiagnostics(error);
357
- }
358
- };
386
+ let emitted = false;
387
+ const emitDiagnostics = (error) => {
388
+ if (!error || emitted)
389
+ return;
390
+ emitted = true;
391
+ try {
392
+ const diagnostics = serializeDiagnosticValue(error, 0, /* @__PURE__ */ new WeakSet());
393
+ const output = `
394
+ [lovable-build-error-diagnostics]
395
+ ${stringifyDiagnosticPayload(diagnostics)}
396
+ [/lovable-build-error-diagnostics]
397
+ `;
398
+ let written = false;
399
+ const write = () => {
400
+ if (written)
401
+ return;
402
+ written = true;
403
+ try {
404
+ process.stderr.write(output);
405
+ } catch {
406
+ }
407
+ };
408
+ setImmediate(write);
409
+ process.once("exit", write);
410
+ } catch {
411
+ }
412
+ };
413
+ return {
414
+ name: BUILD_DIAGNOSTICS_PLUGIN_NAME,
415
+ apply: "build",
416
+ buildEnd(error) {
417
+ emitDiagnostics(error);
418
+ },
419
+ // Rolldown raises output-phase errors after a clean buildEnd; only
420
+ // renderError sees them.
421
+ renderError(error) {
422
+ emitDiagnostics(error);
423
+ }
424
+ };
359
425
  }
360
426
  function devSsrErrorLogger() {
361
- let lastCapture;
362
- const CAPTURE_TTL_MS = 5e3;
363
- const handlerRanFlag = Symbol("lovableHandlerRan");
364
- const capture = (error) => {
365
- lastCapture = {
366
- error,
367
- at: Date.now()
368
- };
369
- };
370
- const consumeCapture = () => {
371
- if (!lastCapture) return void 0;
372
- if (Date.now() - lastCapture.at > CAPTURE_TTL_MS) {
373
- lastCapture = void 0;
374
- return;
375
- }
376
- const { error } = lastCapture;
377
- lastCapture = void 0;
378
- return error;
379
- };
380
- return {
381
- name: "dev-ssr-error-logger",
382
- apply: "serve",
383
- configureServer(server) {
384
- globalThis[SSR_CAPTURE_KEY] = capture;
385
- globalThis[HANDLER_RAN_KEY] = (event) => {
386
- const res = event?.node?.res;
387
- if (res) res[handlerRanFlag] = true;
388
- };
389
- const g = globalThis;
390
- if (typeof g.addEventListener === "function") {
391
- const addListener = g.addEventListener;
392
- addListener("error", (e) => capture(e.error ?? e));
393
- addListener("unhandledrejection", (e) => capture(e.reason));
394
- }
395
- const onUnhandledRejection = (reason) => capture(reason);
396
- process.on("unhandledRejection", onUnhandledRejection);
397
- server.httpServer?.once("close", () => {
398
- process.off("unhandledRejection", onUnhandledRejection);
399
- });
400
- server.middlewares.use((req, res, next) => {
401
- let handled = false;
402
- const handleErrorResponse = () => {
403
- if (handled || res.statusCode < 500) return;
404
- handled = true;
405
- const captured = consumeCapture();
406
- const ranHandler = Boolean(res[handlerRanFlag]);
407
- let source;
408
- if (!ranHandler) source = "vite";
409
- else if (isBuildError(captured)) source = "build";
410
- else source = "app";
411
- if (!res.headersSent) try {
412
- res.setHeader("x-lovable-error-source", source);
413
- } catch {}
414
- const where = [req.method, req.url].filter(Boolean).join(" ") || "the request";
415
- let data;
416
- if (captured instanceof Error) data = {
417
- name: captured.name,
418
- message: captured.message,
419
- stack: captured.stack
420
- };
421
- else if (typeof captured === "string" && captured.length > 0) data = {
422
- name: "Error",
423
- message: captured
424
- };
425
- else if (ranHandler) data = { message: `The app returned ${res.statusCode} while handling ${where}. The error was handled by a route or error boundary, so no stack was captured — check the failing loader/route code and the dev server output.` };
426
- else data = { message: `Dev server returned ${res.statusCode} for ${where} before the app handler ran. This is usually a Vite build/transform error — check the dev server output for the underlying error.` };
427
- try {
428
- server.ws.send({
429
- type: "custom",
430
- event: "server-ssr-error",
431
- data
432
- });
433
- } catch {}
434
- };
435
- const origWriteHead = res.writeHead.bind(res);
436
- res.writeHead = (...args) => {
437
- if (typeof args[0] === "number") res.statusCode = args[0];
438
- handleErrorResponse();
439
- return origWriteHead(...args);
440
- };
441
- const origEnd = res.end.bind(res);
442
- res.end = (...args) => {
443
- handleErrorResponse();
444
- return origEnd(...args);
445
- };
446
- next();
447
- });
448
- },
449
- transform(code, id) {
450
- const normalizedId = id.replace(/\\/g, "/");
451
- if (!(normalizedId.includes("/@tanstack/start-server-core/src/request-response.ts") || normalizedId.includes("/@tanstack/start-server-core/dist/esm/request-response.js"))) return null;
452
- const needle = "handler(request, requestOpts)";
453
- if (!code.includes(needle)) return null;
454
- return code.replace(needle, `(globalThis.${HANDLER_RAN_KEY}?.(typeof h3Event === "undefined" ? undefined : h3Event), Promise.resolve(${needle}).catch((err) => { globalThis.${SSR_CAPTURE_KEY}?.(err); throw err; }))`);
455
- }
456
- };
427
+ let lastCapture;
428
+ const CAPTURE_TTL_MS = 5e3;
429
+ const handlerRanFlag = Symbol("lovableHandlerRan");
430
+ const capture = (error) => {
431
+ lastCapture = { error, at: Date.now() };
432
+ };
433
+ const consumeCapture = () => {
434
+ if (!lastCapture)
435
+ return void 0;
436
+ if (Date.now() - lastCapture.at > CAPTURE_TTL_MS) {
437
+ lastCapture = void 0;
438
+ return void 0;
439
+ }
440
+ const { error } = lastCapture;
441
+ lastCapture = void 0;
442
+ return error;
443
+ };
444
+ return {
445
+ name: "dev-ssr-error-logger",
446
+ apply: "serve",
447
+ configureServer(server) {
448
+ globalThis[SSR_CAPTURE_KEY] = capture;
449
+ globalThis[HANDLER_RAN_KEY] = (event) => {
450
+ const res = event?.node?.res;
451
+ if (res)
452
+ res[handlerRanFlag] = true;
453
+ };
454
+ const g = globalThis;
455
+ if (typeof g.addEventListener === "function") {
456
+ const addListener = g.addEventListener;
457
+ addListener("error", (e) => capture(e.error ?? e));
458
+ addListener("unhandledrejection", (e) => capture(e.reason));
459
+ }
460
+ const onUnhandledRejection = (reason) => capture(reason);
461
+ process.on("unhandledRejection", onUnhandledRejection);
462
+ server.httpServer?.once("close", () => {
463
+ process.off("unhandledRejection", onUnhandledRejection);
464
+ });
465
+ server.middlewares.use((req, res, next) => {
466
+ let handled = false;
467
+ const handleErrorResponse = () => {
468
+ if (handled || res.statusCode < 500)
469
+ return;
470
+ handled = true;
471
+ const captured = consumeCapture();
472
+ const ranHandler = Boolean(res[handlerRanFlag]);
473
+ let source;
474
+ if (!ranHandler) {
475
+ source = "vite";
476
+ } else if (isBuildError(captured)) {
477
+ source = "build";
478
+ } else {
479
+ source = "app";
480
+ }
481
+ if (!res.headersSent) {
482
+ try {
483
+ res.setHeader("x-lovable-error-source", source);
484
+ } catch {
485
+ }
486
+ }
487
+ const where = [req.method, req.url].filter(Boolean).join(" ") || "the request";
488
+ let data;
489
+ if (captured instanceof Error) {
490
+ data = { name: captured.name, message: captured.message, stack: captured.stack };
491
+ } else if (typeof captured === "string" && captured.length > 0) {
492
+ data = { name: "Error", message: captured };
493
+ } else if (ranHandler) {
494
+ data = {
495
+ message: `The app returned ${res.statusCode} while handling ${where}. The error was handled by a route or error boundary, so no stack was captured \u2014 check the failing loader/route code and the dev server output.`
496
+ };
497
+ } else {
498
+ data = {
499
+ message: `Dev server returned ${res.statusCode} for ${where} before the app handler ran. This is usually a Vite build/transform error \u2014 check the dev server output for the underlying error.`
500
+ };
501
+ }
502
+ try {
503
+ server.ws.send({ type: "custom", event: "server-ssr-error", data });
504
+ } catch {
505
+ }
506
+ };
507
+ const origWriteHead = res.writeHead.bind(res);
508
+ res.writeHead = (...args) => {
509
+ if (typeof args[0] === "number")
510
+ res.statusCode = args[0];
511
+ handleErrorResponse();
512
+ return origWriteHead(...args);
513
+ };
514
+ const origEnd = res.end.bind(res);
515
+ res.end = (...args) => {
516
+ handleErrorResponse();
517
+ return origEnd(...args);
518
+ };
519
+ next();
520
+ });
521
+ },
522
+ transform(code, id) {
523
+ const normalizedId = id.replace(/\\/g, "/");
524
+ const isTargetModule = normalizedId.includes("/@tanstack/start-server-core/src/request-response.ts") || normalizedId.includes("/@tanstack/start-server-core/dist/esm/request-response.js");
525
+ if (!isTargetModule) {
526
+ return null;
527
+ }
528
+ const needle = "handler(request, requestOpts)";
529
+ if (!code.includes(needle)) {
530
+ return null;
531
+ }
532
+ return code.replace(
533
+ needle,
534
+ `(globalThis.${HANDLER_RAN_KEY}?.(typeof h3Event === "undefined" ? undefined : h3Event), Promise.resolve(${needle}).catch((err) => { globalThis.${SSR_CAPTURE_KEY}?.(err); throw err; }))`
535
+ );
536
+ }
537
+ };
457
538
  }
458
539
  function devServerFnErrorLogger() {
459
- const HMR_SEND_KEY = "__TANSTACK_SERVER_FN_HMR_SEND__";
460
- return {
461
- name: "dev-server-fn-error-logger",
462
- apply: "serve",
463
- enforce: "pre",
464
- configureServer(server) {
465
- globalThis[HMR_SEND_KEY] = (data) => {
466
- server.ws.send({
467
- type: "custom",
468
- event: "server-fn-error",
469
- data
470
- });
471
- };
472
- },
473
- transform(code, id) {
474
- const normalizedId = id.replace(/\\/g, "/");
475
- if (!(normalizedId.includes("/@tanstack/start-server-core/src/server-functions-handler.ts") || normalizedId.includes("/@tanstack/start-server-core/dist/esm/server-functions-handler.js"))) return null;
476
- const needle = "const unwrapped = res.result || res.error";
477
- if (!code.includes(needle)) return null;
478
- return code.replace(needle, `${needle}
540
+ const HMR_SEND_KEY = "__TANSTACK_SERVER_FN_HMR_SEND__";
541
+ return {
542
+ name: "dev-server-fn-error-logger",
543
+ apply: "serve",
544
+ enforce: "pre",
545
+ configureServer(server) {
546
+ globalThis[HMR_SEND_KEY] = (data) => {
547
+ server.ws.send({
548
+ type: "custom",
549
+ event: "server-fn-error",
550
+ data
551
+ });
552
+ };
553
+ },
554
+ transform(code, id) {
555
+ const normalizedId = id.replace(/\\/g, "/");
556
+ const isTargetModule = normalizedId.includes("/@tanstack/start-server-core/src/server-functions-handler.ts") || normalizedId.includes("/@tanstack/start-server-core/dist/esm/server-functions-handler.js");
557
+ if (!isTargetModule) {
558
+ return null;
559
+ }
560
+ const needle = "const unwrapped = res.result || res.error";
561
+ if (!code.includes(needle)) {
562
+ return null;
563
+ }
564
+ return code.replace(
565
+ needle,
566
+ `${needle}
479
567
 
480
568
  if (res?.error) {
481
569
  const err = res.error
@@ -489,155 +577,191 @@ function devServerFnErrorLogger() {
489
577
  stack: typeof err?.stack === 'string' ? err.stack : undefined,
490
578
  }
491
579
  globalThis.${HMR_SEND_KEY}?.(payload)
492
- }`);
493
- }
494
- };
580
+ }`
581
+ );
582
+ }
583
+ };
495
584
  }
496
585
  function defineConfig(configOrOptions = {}) {
497
- return async (env) => {
498
- const { command, mode } = env;
499
- const isSandbox = isSandboxEnvironment();
500
- const isDevBuild = command === "build" && mode === "development";
501
- let options;
502
- if (typeof configOrOptions === "function") options = { vite: await Promise.resolve(configOrOptions(env)) };
503
- else if (configOrOptions instanceof Promise) options = { vite: await configOrOptions };
504
- else {
505
- const optionObject = configOrOptions && typeof configOrOptions === "object" ? configOrOptions : {};
506
- if ("cloudflare" in optionObject) if (optionObject.cloudflare === false && !("nitro" in optionObject)) {
507
- optionObject.nitro = false;
508
- logWarning("`cloudflare: false` is deprecated in v2 — migrated to `nitro: false`. Update your config to drop the warning.");
509
- } else logWarning("The `cloudflare` option was removed in v2. Use `nitro: false` to disable the deploy plugin, or `nitro: { preset: '...' }` to configure it. The `cloudflare` setting is being ignored.");
510
- options = "vite" in optionObject || "serverFnErrorLogger" in optionObject || "ssrErrorLogger" in optionObject || "nitro" in optionObject || "tanstackStart" in optionObject || "react" in optionObject || "envDefine" in optionObject || "hmrGate" in optionObject ? optionObject : { vite: optionObject };
511
- }
512
- const internalPlugins = [];
513
- if (mode === "development") {
514
- const { devtools } = await import("@tanstack/devtools-vite");
515
- internalPlugins.push(devtools({
516
- logging: false,
517
- eventBusConfig: { enabled: false },
518
- enhancedLogs: { enabled: false },
519
- consolePiping: { enabled: false },
520
- removeDevtoolsOnBuild: false,
521
- injectSource: { enabled: true }
522
- }));
523
- }
524
- const tailwindcss = (await import("@tailwindcss/vite")).default;
525
- internalPlugins.push(tailwindcss());
526
- const tsConfigPaths = (await import("vite-tsconfig-paths")).default;
527
- internalPlugins.push(tsConfigPaths({ projects: ["./tsconfig.json"] }));
528
- if (options.serverFnErrorLogger !== false) internalPlugins.push(devServerFnErrorLogger());
529
- if (options.ssrErrorLogger !== false) internalPlugins.push(devSsrErrorLogger());
530
- if (command === "build" && isSandbox) internalPlugins.push(lovableBuildErrorDiagnostics());
531
- const { tanstackStart } = await import("@tanstack/react-start/plugin/vite");
532
- const tanstackStartOptions = mergeConfig({ importProtection: {
533
- behavior: "error",
534
- client: {
535
- files: ["**/server/**"],
536
- specifiers: ["server-only"]
537
- }
538
- } }, options.tanstackStart ?? {});
539
- internalPlugins.push(tanstackStart(tanstackStartOptions));
540
- const explicitNitro = options.nitro === true || typeof options.nitro === "object" && options.nitro !== null;
541
- if (options.nitro !== false && command === "build") {
542
- let nitroMod;
543
- try {
544
- nitroMod = await import("nitro/vite");
545
- } catch (err) {
546
- if (explicitNitro || isSandbox) {
547
- const reason = explicitNitro ? `an explicit \`nitro\` option (${options.nitro === true ? "true" : "{ ... }"}) was set in vite.config.ts` : "running inside a Lovable sandbox";
548
- throw new Error(`[@lovable.dev/vite-tanstack-config] ${reason}, but the \`nitro\` package isn't installed. Add \`nitro\` to your devDependencies (matches peerDependency >=3.0.260429-beta). Original import error: ${err instanceof Error ? err.message : String(err)}`);
549
- }
550
- }
551
- if (nitroMod) {
552
- const { nitro } = nitroMod;
553
- const userNitroOpts = typeof options.nitro === "object" && options.nitro ? options.nitro : {};
554
- const nitroOpts = {
555
- defaultPreset: "cloudflare-module",
556
- ...userNitroOpts
557
- };
558
- if (isSandbox) {
559
- delete nitroOpts.defaultPreset;
560
- nitroOpts.preset = "cloudflare-module";
561
- nitroOpts.output = {
562
- dir: "dist",
563
- serverDir: "dist/server",
564
- publicDir: "dist/client"
565
- };
566
- nitroOpts.cloudflare = {
567
- nodeCompat: true,
568
- deployConfig: true,
569
- ...userNitroOpts.cloudflare
570
- };
571
- } else if (!nitroOpts.preset && !await nitroSupportsDefaultPreset()) logWarning("The installed `nitro` predates the `defaultPreset` option (3.0.260603-beta), so the Cloudflare fallback is ignored and a zero-config build may target Node instead. Upgrade nitro to >=3.0.260603-beta, or set an explicit `nitro: { preset: \"cloudflare-module\" }`.");
572
- internalPlugins.push(nitro(nitroOpts));
573
- }
574
- }
575
- const viteReact = (await import("@vitejs/plugin-react")).default;
576
- internalPlugins.push(viteReact(options.react));
577
- if (command === "serve") {
578
- const hmrGateOpt = options.hmrGate;
579
- if (isSandbox ? hmrGateOpt !== false : !!hmrGateOpt) {
580
- const { hmrGatePlugin } = await import("@lovable.dev/vite-plugin-hmr-gate");
581
- const hmrGateOptions = typeof hmrGateOpt === "object" ? hmrGateOpt : {};
582
- internalPlugins.push(hmrGatePlugin(hmrGateOptions));
583
- }
584
- }
585
- if (command === "serve" && isSandbox) {
586
- const { devServerBridgePlugin } = await import("@lovable.dev/vite-plugin-dev-server-bridge");
587
- internalPlugins.push(devServerBridgePlugin());
588
- }
589
- if (command === "serve") internalPlugins.push(lovableAssetsProxyPlugin());
590
- let envDefine = {};
591
- if (options.envDefine !== false) {
592
- const loadedEnv = loadEnv(mode, process.cwd(), "VITE_");
593
- for (const [key, value] of Object.entries(loadedEnv)) envDefine[`import.meta.env.${key}`] = JSON.stringify(value);
594
- }
595
- let config = {
596
- define: envDefine,
597
- ...isDevBuild ? {
598
- environments: { client: { define: { "process.env.NODE_ENV": JSON.stringify("development") } } },
599
- esbuild: { keepNames: true }
600
- } : {},
601
- css: { transformer: "lightningcss" },
602
- resolve: {
603
- alias: { "@": `${process.cwd()}/src` },
604
- dedupe: [
605
- "react",
606
- "react-dom",
607
- "react/jsx-runtime",
608
- "react/jsx-dev-runtime",
609
- "@tanstack/react-query",
610
- "@tanstack/query-core"
611
- ]
612
- },
613
- optimizeDeps: {
614
- include: [
615
- "react",
616
- "react-dom",
617
- "react-dom/client",
618
- "react/jsx-runtime",
619
- "react/jsx-dev-runtime"
620
- ],
621
- ignoreOutdatedRequests: true
622
- },
623
- plugins: [...internalPlugins, ...options.plugins ?? []]
624
- };
625
- if (options.vite) config = mergeConfig(config, options.vite);
626
- if (isSandbox) {
627
- validateConfig(config);
628
- config = cleanServerConfig(mergeConfig(config, { server: {
629
- host: "::",
630
- port: 8080,
631
- strictPort: true,
632
- watch: { ignored: [...TOOL_MANAGED_WATCH_IGNORES] }
633
- } }));
634
- } else config = mergeConfig({ server: {
635
- host: "::",
636
- port: 8080
637
- } }, config);
638
- if (isSandbox) return config;
639
- return applyWatchDebounceDefaults(config);
640
- };
586
+ return async (env) => {
587
+ const { command, mode } = env;
588
+ const isSandbox = isSandboxEnvironment();
589
+ const isDevBuild = command === "build" && mode === "development";
590
+ let options;
591
+ if (typeof configOrOptions === "function") {
592
+ const userConfig = await Promise.resolve(configOrOptions(env));
593
+ options = { vite: userConfig };
594
+ } else if (configOrOptions instanceof Promise) {
595
+ options = { vite: await configOrOptions };
596
+ } else {
597
+ const optionObject = configOrOptions && typeof configOrOptions === "object" ? configOrOptions : {};
598
+ if ("cloudflare" in optionObject) {
599
+ if (optionObject.cloudflare === false && !("nitro" in optionObject)) {
600
+ optionObject.nitro = false;
601
+ logWarning(
602
+ "`cloudflare: false` is deprecated in v2 \u2014 migrated to `nitro: false`. Update your config to drop the warning."
603
+ );
604
+ } else {
605
+ logWarning(
606
+ "The `cloudflare` option was removed in v2. Use `nitro: false` to disable the deploy plugin, or `nitro: { preset: '...' }` to configure it. The `cloudflare` setting is being ignored."
607
+ );
608
+ }
609
+ }
610
+ const hasLovableKey = "vite" in optionObject || "serverFnErrorLogger" in optionObject || "ssrErrorLogger" in optionObject || "nitro" in optionObject || "tanstackStart" in optionObject || "react" in optionObject || "envDefine" in optionObject || "hmrGate" in optionObject;
611
+ options = hasLovableKey ? optionObject : { vite: optionObject };
612
+ }
613
+ const internalPlugins = [];
614
+ if (mode === "development") {
615
+ const { devtools } = await import("@tanstack/devtools-vite");
616
+ internalPlugins.push(
617
+ devtools({
618
+ logging: false,
619
+ eventBusConfig: { enabled: false },
620
+ enhancedLogs: { enabled: false },
621
+ consolePiping: { enabled: false },
622
+ removeDevtoolsOnBuild: false,
623
+ injectSource: { enabled: true }
624
+ })
625
+ );
626
+ }
627
+ const tailwindcss = (await import("@tailwindcss/vite")).default;
628
+ internalPlugins.push(tailwindcss());
629
+ const tsConfigPaths = (await import("vite-tsconfig-paths")).default;
630
+ internalPlugins.push(tsConfigPaths({ projects: ["./tsconfig.json"] }));
631
+ if (options.serverFnErrorLogger !== false) {
632
+ internalPlugins.push(devServerFnErrorLogger());
633
+ }
634
+ if (options.ssrErrorLogger !== false) {
635
+ internalPlugins.push(devSsrErrorLogger());
636
+ }
637
+ if (command === "build" && isSandbox) {
638
+ internalPlugins.push(lovableBuildErrorDiagnostics());
639
+ }
640
+ const { tanstackStart } = await import("@tanstack/react-start/plugin/vite");
641
+ const tanstackStartDefaults = {
642
+ importProtection: {
643
+ behavior: "error",
644
+ client: {
645
+ files: ["**/server/**"],
646
+ specifiers: ["server-only"]
647
+ }
648
+ }
649
+ };
650
+ const tanstackStartOptions = mergeConfig(tanstackStartDefaults, options.tanstackStart ?? {});
651
+ internalPlugins.push(tanstackStart(tanstackStartOptions));
652
+ const explicitNitro = options.nitro === true || typeof options.nitro === "object" && options.nitro !== null;
653
+ const shouldRunNitro = options.nitro !== false && command === "build";
654
+ if (shouldRunNitro) {
655
+ let nitroMod;
656
+ try {
657
+ nitroMod = await import("nitro/vite");
658
+ } catch (err) {
659
+ if (explicitNitro || isSandbox) {
660
+ const reason = explicitNitro ? `an explicit \`nitro\` option (${options.nitro === true ? "true" : "{ ... }"}) was set in vite.config.ts` : "running inside a Lovable sandbox";
661
+ throw new Error(
662
+ `[@lovable.dev/vite-tanstack-config] ${reason}, but the \`nitro\` package isn't installed. Add \`nitro\` to your devDependencies (matches peerDependency >=3.0.260429-beta). Original import error: ${err instanceof Error ? err.message : String(err)}`
663
+ );
664
+ }
665
+ }
666
+ if (nitroMod) {
667
+ const { nitro } = nitroMod;
668
+ const userNitroOpts = typeof options.nitro === "object" && options.nitro ? options.nitro : {};
669
+ const nitroOpts = { defaultPreset: "cloudflare-module", ...userNitroOpts };
670
+ if (isSandbox) {
671
+ delete nitroOpts.defaultPreset;
672
+ nitroOpts.preset = "cloudflare-module";
673
+ nitroOpts.output = { dir: "dist", serverDir: "dist/server", publicDir: "dist/client" };
674
+ nitroOpts.cloudflare = { nodeCompat: true, deployConfig: true, ...userNitroOpts.cloudflare };
675
+ } else if (!nitroOpts.preset && !await nitroSupportsDefaultPreset()) {
676
+ logWarning(
677
+ 'The installed `nitro` predates the `defaultPreset` option (3.0.260603-beta), so the Cloudflare fallback is ignored and a zero-config build may target Node instead. Upgrade nitro to >=3.0.260603-beta, or set an explicit `nitro: { preset: "cloudflare-module" }`.'
678
+ );
679
+ }
680
+ internalPlugins.push(nitro(nitroOpts));
681
+ }
682
+ }
683
+ const viteReact = (await import("@vitejs/plugin-react")).default;
684
+ internalPlugins.push(viteReact(options.react));
685
+ if (command === "serve") {
686
+ const hmrGateOpt = options.hmrGate;
687
+ const enableHmrGate = isSandbox ? hmrGateOpt !== false : !!hmrGateOpt;
688
+ if (enableHmrGate) {
689
+ const { hmrGatePlugin } = await import("@lovable.dev/vite-plugin-hmr-gate");
690
+ const hmrGateOptions = typeof hmrGateOpt === "object" ? hmrGateOpt : {};
691
+ internalPlugins.push(hmrGatePlugin(hmrGateOptions));
692
+ }
693
+ }
694
+ if (command === "serve" && isSandbox) {
695
+ const { devServerBridgePlugin } = await import("@lovable.dev/vite-plugin-dev-server-bridge");
696
+ internalPlugins.push(devServerBridgePlugin());
697
+ }
698
+ if (command === "serve") {
699
+ internalPlugins.push(lovableAssetsProxyPlugin());
700
+ }
701
+ let envDefine = {};
702
+ if (options.envDefine !== false) {
703
+ const loadedEnv = loadEnv(mode, process.cwd(), "VITE_");
704
+ for (const [key, value] of Object.entries(loadedEnv)) {
705
+ envDefine[`import.meta.env.${key}`] = JSON.stringify(value);
706
+ }
707
+ }
708
+ let config = {
709
+ define: envDefine,
710
+ // Client-scoped so React DevTools gets the dev react-dom; a global NODE_ENV
711
+ // flip would emit jsxDEV, which the react-server SSR runtime can't resolve.
712
+ ...isDevBuild ? {
713
+ environments: { client: { define: { "process.env.NODE_ENV": JSON.stringify("development") } } },
714
+ esbuild: { keepNames: true }
715
+ } : {},
716
+ // Match the build's CSS pipeline in dev. Vite uses PostCSS in dev and only
717
+ // runs Lightning CSS at build, so build-time transforms (e.g. collapsing a
718
+ // hand-written `-webkit-backdrop-filter` to the prefixed form Chrome ignores)
719
+ // break the built/static output while the dev preview looks fine. Running
720
+ // Lightning CSS in both keeps the preview honest.
721
+ css: { transformer: "lightningcss" },
722
+ resolve: {
723
+ alias: {
724
+ "@": `${process.cwd()}/src`
725
+ },
726
+ dedupe: [
727
+ "react",
728
+ "react-dom",
729
+ "react/jsx-runtime",
730
+ "react/jsx-dev-runtime",
731
+ "@tanstack/react-query",
732
+ "@tanstack/query-core"
733
+ ]
734
+ },
735
+ // Dep re-optimization rotates the optimized-dep hash and 504s tabs holding
736
+ // the old one; pre-bundle the always-present client deps + tolerate stale
737
+ // requests. React core only — including @tanstack/react-start would pull its
738
+ // node:async_hooks server entry into the client bundle and crash hydration.
739
+ optimizeDeps: {
740
+ include: ["react", "react-dom", "react-dom/client", "react/jsx-runtime", "react/jsx-dev-runtime"],
741
+ ignoreOutdatedRequests: true
742
+ },
743
+ plugins: [...internalPlugins, ...options.plugins ?? []]
744
+ };
745
+ if (options.vite) {
746
+ config = mergeConfig(config, options.vite);
747
+ }
748
+ if (isSandbox) {
749
+ validateConfig(config);
750
+ config = cleanServerConfig(
751
+ mergeConfig(config, {
752
+ server: { host: "::", port: 8080, strictPort: true, watch: { ignored: [...TOOL_MANAGED_WATCH_IGNORES] } }
753
+ })
754
+ );
755
+ } else {
756
+ config = mergeConfig({ server: { host: "::", port: 8080 } }, config);
757
+ }
758
+ if (isSandbox) {
759
+ return config;
760
+ }
761
+ return applyWatchDebounceDefaults(config);
762
+ };
641
763
  }
642
- //#endregion
643
- export { defineConfig, lovableAssetsProxyPlugin };
764
+ export {
765
+ defineConfig,
766
+ lovableAssetsProxyPlugin
767
+ };