@lovable.dev/vite-tanstack-config 2.2.0 → 2.3.0

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.
Files changed (3) hide show
  1. package/dist/index.cjs +73 -19
  2. package/dist/index.js +73 -19
  3. package/package.json +14 -13
package/dist/index.cjs CHANGED
@@ -131,9 +131,28 @@ function applyWatchDebounceDefaults(config) {
131
131
  });
132
132
  }
133
133
  var SSR_CAPTURE_KEY = "__LOVABLE_TANSTACK_CAPTURE_SSR_ERROR__";
134
+ var HANDLER_RAN_KEY = "__LOVABLE_TANSTACK_HANDLER_RAN__";
135
+ var BUILD_ERROR_MESSAGE = /transform failed with \d+ error|failed to parse source/i;
136
+ function isBuildError(error) {
137
+ if (typeof error === "string")
138
+ return BUILD_ERROR_MESSAGE.test(error);
139
+ if (!error || typeof error !== "object")
140
+ return false;
141
+ const e = error;
142
+ if (typeof e.plugin === "string" && (e.plugin.startsWith("vite:") || e.plugin.startsWith("esbuild:")))
143
+ return true;
144
+ if (e.frame != null && e.loc != null)
145
+ return true;
146
+ if (Array.isArray(e.errors) && e.errors.length > 0 && e.errors.every((x) => x != null && typeof x === "object" && "text" in x && "location" in x)) {
147
+ return true;
148
+ }
149
+ const message = typeof e.message === "string" ? e.message : "";
150
+ return BUILD_ERROR_MESSAGE.test(message);
151
+ }
134
152
  function devSsrErrorLogger() {
135
153
  let lastCapture;
136
154
  const CAPTURE_TTL_MS = 5e3;
155
+ const handlerRanFlag = Symbol("lovableHandlerRan");
137
156
  const capture = (error) => {
138
157
  lastCapture = { error, at: Date.now() };
139
158
  };
@@ -153,6 +172,11 @@ function devSsrErrorLogger() {
153
172
  apply: "serve",
154
173
  configureServer(server) {
155
174
  globalThis[SSR_CAPTURE_KEY] = capture;
175
+ globalThis[HANDLER_RAN_KEY] = (event) => {
176
+ const res = event?.node?.res;
177
+ if (res)
178
+ res[handlerRanFlag] = true;
179
+ };
156
180
  const g = globalThis;
157
181
  if (typeof g.addEventListener === "function") {
158
182
  const addListener = g.addEventListener;
@@ -164,28 +188,58 @@ function devSsrErrorLogger() {
164
188
  server.httpServer?.once("close", () => {
165
189
  process.off("unhandledRejection", onUnhandledRejection);
166
190
  });
167
- server.middlewares.use((_req, res, next) => {
168
- const origEnd = res.end.bind(res);
169
- res.end = (...args) => {
170
- if (res.statusCode >= 500) {
171
- const captured = consumeCapture();
172
- let err;
173
- if (captured instanceof Error) {
174
- err = captured;
175
- } else if (typeof captured === "string" && captured.length > 0) {
176
- err = new Error(captured);
177
- } else {
178
- err = null;
179
- }
191
+ server.middlewares.use((req, res, next) => {
192
+ let handled = false;
193
+ const handleErrorResponse = () => {
194
+ if (handled || res.statusCode < 500)
195
+ return;
196
+ handled = true;
197
+ const captured = consumeCapture();
198
+ const ranHandler = Boolean(res[handlerRanFlag]);
199
+ let source;
200
+ if (!ranHandler) {
201
+ source = "vite";
202
+ } else if (isBuildError(captured)) {
203
+ source = "build";
204
+ } else {
205
+ source = "app";
206
+ }
207
+ if (!res.headersSent) {
180
208
  try {
181
- server.ws.send({
182
- type: "custom",
183
- event: "server-ssr-error",
184
- data: err ? { name: err.name, message: err.message, stack: err.stack } : { name: "Error", message: "SSR rendering failed" }
185
- });
209
+ res.setHeader("x-lovable-error-source", source);
186
210
  } catch {
187
211
  }
188
212
  }
213
+ const where = [req.method, req.url].filter(Boolean).join(" ") || "the request";
214
+ let data;
215
+ if (captured instanceof Error) {
216
+ data = { name: captured.name, message: captured.message, stack: captured.stack };
217
+ } else if (typeof captured === "string" && captured.length > 0) {
218
+ data = { name: "Error", message: captured };
219
+ } else if (ranHandler) {
220
+ data = {
221
+ 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.`
222
+ };
223
+ } else {
224
+ data = {
225
+ 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.`
226
+ };
227
+ }
228
+ try {
229
+ server.ws.send({ type: "custom", event: "server-ssr-error", data });
230
+ } catch {
231
+ }
232
+ };
233
+ const origWriteHead = res.writeHead.bind(res);
234
+ res.writeHead = (...args) => {
235
+ if (typeof args[0] === "number")
236
+ res.statusCode = args[0];
237
+ handleErrorResponse();
238
+ return origWriteHead(...args);
239
+ };
240
+ const origEnd = res.end.bind(res);
241
+ res.end = (...args) => {
242
+ handleErrorResponse();
189
243
  return origEnd(...args);
190
244
  };
191
245
  next();
@@ -203,7 +257,7 @@ function devSsrErrorLogger() {
203
257
  }
204
258
  return code.replace(
205
259
  needle,
206
- `Promise.resolve(${needle}).catch((err) => { globalThis.${SSR_CAPTURE_KEY}?.(err); throw err; })`
260
+ `(globalThis.${HANDLER_RAN_KEY}?.(typeof h3Event === "undefined" ? undefined : h3Event), Promise.resolve(${needle}).catch((err) => { globalThis.${SSR_CAPTURE_KEY}?.(err); throw err; }))`
207
261
  );
208
262
  }
209
263
  };
package/dist/index.js CHANGED
@@ -97,9 +97,28 @@ function applyWatchDebounceDefaults(config) {
97
97
  });
98
98
  }
99
99
  var SSR_CAPTURE_KEY = "__LOVABLE_TANSTACK_CAPTURE_SSR_ERROR__";
100
+ var HANDLER_RAN_KEY = "__LOVABLE_TANSTACK_HANDLER_RAN__";
101
+ var BUILD_ERROR_MESSAGE = /transform failed with \d+ error|failed to parse source/i;
102
+ function isBuildError(error) {
103
+ if (typeof error === "string")
104
+ return BUILD_ERROR_MESSAGE.test(error);
105
+ if (!error || typeof error !== "object")
106
+ return false;
107
+ const e = error;
108
+ if (typeof e.plugin === "string" && (e.plugin.startsWith("vite:") || e.plugin.startsWith("esbuild:")))
109
+ return true;
110
+ if (e.frame != null && e.loc != null)
111
+ return true;
112
+ if (Array.isArray(e.errors) && e.errors.length > 0 && e.errors.every((x) => x != null && typeof x === "object" && "text" in x && "location" in x)) {
113
+ return true;
114
+ }
115
+ const message = typeof e.message === "string" ? e.message : "";
116
+ return BUILD_ERROR_MESSAGE.test(message);
117
+ }
100
118
  function devSsrErrorLogger() {
101
119
  let lastCapture;
102
120
  const CAPTURE_TTL_MS = 5e3;
121
+ const handlerRanFlag = Symbol("lovableHandlerRan");
103
122
  const capture = (error) => {
104
123
  lastCapture = { error, at: Date.now() };
105
124
  };
@@ -119,6 +138,11 @@ function devSsrErrorLogger() {
119
138
  apply: "serve",
120
139
  configureServer(server) {
121
140
  globalThis[SSR_CAPTURE_KEY] = capture;
141
+ globalThis[HANDLER_RAN_KEY] = (event) => {
142
+ const res = event?.node?.res;
143
+ if (res)
144
+ res[handlerRanFlag] = true;
145
+ };
122
146
  const g = globalThis;
123
147
  if (typeof g.addEventListener === "function") {
124
148
  const addListener = g.addEventListener;
@@ -130,28 +154,58 @@ function devSsrErrorLogger() {
130
154
  server.httpServer?.once("close", () => {
131
155
  process.off("unhandledRejection", onUnhandledRejection);
132
156
  });
133
- server.middlewares.use((_req, res, next) => {
134
- const origEnd = res.end.bind(res);
135
- res.end = (...args) => {
136
- if (res.statusCode >= 500) {
137
- const captured = consumeCapture();
138
- let err;
139
- if (captured instanceof Error) {
140
- err = captured;
141
- } else if (typeof captured === "string" && captured.length > 0) {
142
- err = new Error(captured);
143
- } else {
144
- err = null;
145
- }
157
+ server.middlewares.use((req, res, next) => {
158
+ let handled = false;
159
+ const handleErrorResponse = () => {
160
+ if (handled || res.statusCode < 500)
161
+ return;
162
+ handled = true;
163
+ const captured = consumeCapture();
164
+ const ranHandler = Boolean(res[handlerRanFlag]);
165
+ let source;
166
+ if (!ranHandler) {
167
+ source = "vite";
168
+ } else if (isBuildError(captured)) {
169
+ source = "build";
170
+ } else {
171
+ source = "app";
172
+ }
173
+ if (!res.headersSent) {
146
174
  try {
147
- server.ws.send({
148
- type: "custom",
149
- event: "server-ssr-error",
150
- data: err ? { name: err.name, message: err.message, stack: err.stack } : { name: "Error", message: "SSR rendering failed" }
151
- });
175
+ res.setHeader("x-lovable-error-source", source);
152
176
  } catch {
153
177
  }
154
178
  }
179
+ const where = [req.method, req.url].filter(Boolean).join(" ") || "the request";
180
+ let data;
181
+ if (captured instanceof Error) {
182
+ data = { name: captured.name, message: captured.message, stack: captured.stack };
183
+ } else if (typeof captured === "string" && captured.length > 0) {
184
+ data = { name: "Error", message: captured };
185
+ } else if (ranHandler) {
186
+ data = {
187
+ 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.`
188
+ };
189
+ } else {
190
+ data = {
191
+ 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.`
192
+ };
193
+ }
194
+ try {
195
+ server.ws.send({ type: "custom", event: "server-ssr-error", data });
196
+ } catch {
197
+ }
198
+ };
199
+ const origWriteHead = res.writeHead.bind(res);
200
+ res.writeHead = (...args) => {
201
+ if (typeof args[0] === "number")
202
+ res.statusCode = args[0];
203
+ handleErrorResponse();
204
+ return origWriteHead(...args);
205
+ };
206
+ const origEnd = res.end.bind(res);
207
+ res.end = (...args) => {
208
+ handleErrorResponse();
155
209
  return origEnd(...args);
156
210
  };
157
211
  next();
@@ -169,7 +223,7 @@ function devSsrErrorLogger() {
169
223
  }
170
224
  return code.replace(
171
225
  needle,
172
- `Promise.resolve(${needle}).catch((err) => { globalThis.${SSR_CAPTURE_KEY}?.(err); throw err; })`
226
+ `(globalThis.${HANDLER_RAN_KEY}?.(typeof h3Event === "undefined" ? undefined : h3Event), Promise.resolve(${needle}).catch((err) => { globalThis.${SSR_CAPTURE_KEY}?.(err); throw err; }))`
173
227
  );
174
228
  }
175
229
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lovable.dev/vite-tanstack-config",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Vite config wrapper for Lovable TanStack Start projects",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -9,6 +9,15 @@
9
9
  "files": [
10
10
  "dist"
11
11
  ],
12
+ "scripts": {
13
+ "build": "tsup src/index.ts --format cjs,esm --dts --outDir dist",
14
+ "typecheck": "tsgo --noEmit",
15
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
16
+ "test": "vitest",
17
+ "prepublishOnly": "npm run clean && npm run build",
18
+ "clean": "rimraf dist",
19
+ "coverage": "vitest run --coverage"
20
+ },
12
21
  "keywords": [
13
22
  "vite",
14
23
  "tanstack",
@@ -40,20 +49,12 @@
40
49
  "@tanstack/react-start": "^1.162.9",
41
50
  "@vitejs/plugin-react": "^4.5.2",
42
51
  "@tailwindcss/vite": "^4.2.1",
43
- "@types/node": "22.13.13",
52
+ "@types/node": "catalog:",
44
53
  "rimraf": "^5.0.0",
45
54
  "tsup": "^7.2.0",
46
- "typescript": "^5.9.3",
55
+ "typescript": "catalog:",
47
56
  "vite": "^7.3.1",
48
57
  "vite-tsconfig-paths": "^6.0.2",
49
- "vitest": "^4.1.5"
50
- },
51
- "scripts": {
52
- "build": "tsup src/index.ts --format cjs,esm --dts --outDir dist",
53
- "typecheck": "tsgo --noEmit",
54
- "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
55
- "test": "vitest",
56
- "clean": "rimraf dist",
57
- "coverage": "vitest run --coverage"
58
+ "vitest": "catalog:"
58
59
  }
59
- }
60
+ }