@lovable.dev/vite-tanstack-config 1.5.1 → 1.7.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.
package/dist/index.cjs CHANGED
@@ -130,6 +130,84 @@ function applyWatchDebounceDefaults(config) {
130
130
  }
131
131
  });
132
132
  }
133
+ var SSR_CAPTURE_KEY = "__LOVABLE_TANSTACK_CAPTURE_SSR_ERROR__";
134
+ function devSsrErrorLogger() {
135
+ let lastCapture;
136
+ const CAPTURE_TTL_MS = 5e3;
137
+ const capture = (error) => {
138
+ lastCapture = { error, at: Date.now() };
139
+ };
140
+ const consumeCapture = () => {
141
+ if (!lastCapture)
142
+ return void 0;
143
+ if (Date.now() - lastCapture.at > CAPTURE_TTL_MS) {
144
+ lastCapture = void 0;
145
+ return void 0;
146
+ }
147
+ const { error } = lastCapture;
148
+ lastCapture = void 0;
149
+ return error;
150
+ };
151
+ return {
152
+ name: "dev-ssr-error-logger",
153
+ apply: "serve",
154
+ configureServer(server) {
155
+ globalThis[SSR_CAPTURE_KEY] = capture;
156
+ const g = globalThis;
157
+ if (typeof g.addEventListener === "function") {
158
+ const addListener = g.addEventListener;
159
+ addListener("error", (e) => capture(e.error ?? e));
160
+ addListener("unhandledrejection", (e) => capture(e.reason));
161
+ }
162
+ const onUnhandledRejection = (reason) => capture(reason);
163
+ process.on("unhandledRejection", onUnhandledRejection);
164
+ server.httpServer?.once("close", () => {
165
+ process.off("unhandledRejection", onUnhandledRejection);
166
+ });
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
+ }
180
+ 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
+ });
186
+ } catch {
187
+ }
188
+ }
189
+ return origEnd(...args);
190
+ };
191
+ next();
192
+ });
193
+ },
194
+ transform(code, id) {
195
+ const normalizedId = id.replace(/\\/g, "/");
196
+ const isTargetModule = normalizedId.includes("/@tanstack/start-server-core/src/request-response.ts") || normalizedId.includes("/@tanstack/start-server-core/dist/esm/request-response.js");
197
+ if (!isTargetModule) {
198
+ return null;
199
+ }
200
+ const needle = "handler(request, requestOpts)";
201
+ if (!code.includes(needle)) {
202
+ return null;
203
+ }
204
+ return code.replace(
205
+ needle,
206
+ `Promise.resolve(${needle}).catch((err) => { globalThis.${SSR_CAPTURE_KEY}?.(err); throw err; })`
207
+ );
208
+ }
209
+ };
210
+ }
133
211
  function devServerFnErrorLogger() {
134
212
  const HMR_SEND_KEY = "__TANSTACK_SERVER_FN_HMR_SEND__";
135
213
  return {
@@ -188,7 +266,7 @@ function defineConfig(configOrOptions = {}) {
188
266
  options = { vite: await configOrOptions };
189
267
  } else {
190
268
  const optionObject = configOrOptions && typeof configOrOptions === "object" ? configOrOptions : {};
191
- const hasLovableKey = "vite" in optionObject || "serverFnErrorLogger" in optionObject || "cloudflare" in optionObject || "tanstackStart" in optionObject || "react" in optionObject || "envDefine" in optionObject || "hmrGate" in optionObject;
269
+ const hasLovableKey = "vite" in optionObject || "serverFnErrorLogger" in optionObject || "ssrErrorLogger" in optionObject || "cloudflare" in optionObject || "tanstackStart" in optionObject || "react" in optionObject || "envDefine" in optionObject || "hmrGate" in optionObject;
192
270
  options = hasLovableKey ? optionObject : { vite: optionObject };
193
271
  }
194
272
  const internalPlugins = [];
@@ -199,6 +277,9 @@ function defineConfig(configOrOptions = {}) {
199
277
  if (options.serverFnErrorLogger !== false) {
200
278
  internalPlugins.push(devServerFnErrorLogger());
201
279
  }
280
+ if (options.ssrErrorLogger !== false) {
281
+ internalPlugins.push(devSsrErrorLogger());
282
+ }
202
283
  if (options.cloudflare !== false && command === "build") {
203
284
  try {
204
285
  const { cloudflare } = await import("@cloudflare/vite-plugin");
package/dist/index.d.cts CHANGED
@@ -4,6 +4,7 @@ interface LovableViteTanstackOptions {
4
4
  plugins?: PluginOption[];
5
5
  vite?: UserConfig;
6
6
  serverFnErrorLogger?: boolean;
7
+ ssrErrorLogger?: boolean;
7
8
  cloudflare?: Record<string, unknown> | false;
8
9
  /** Options forwarded to tanstackStart(). */
9
10
  tanstackStart?: Record<string, unknown>;
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ interface LovableViteTanstackOptions {
4
4
  plugins?: PluginOption[];
5
5
  vite?: UserConfig;
6
6
  serverFnErrorLogger?: boolean;
7
+ ssrErrorLogger?: boolean;
7
8
  cloudflare?: Record<string, unknown> | false;
8
9
  /** Options forwarded to tanstackStart(). */
9
10
  tanstackStart?: Record<string, unknown>;
package/dist/index.js CHANGED
@@ -96,6 +96,84 @@ function applyWatchDebounceDefaults(config) {
96
96
  }
97
97
  });
98
98
  }
99
+ var SSR_CAPTURE_KEY = "__LOVABLE_TANSTACK_CAPTURE_SSR_ERROR__";
100
+ function devSsrErrorLogger() {
101
+ let lastCapture;
102
+ const CAPTURE_TTL_MS = 5e3;
103
+ const capture = (error) => {
104
+ lastCapture = { error, at: Date.now() };
105
+ };
106
+ const consumeCapture = () => {
107
+ if (!lastCapture)
108
+ return void 0;
109
+ if (Date.now() - lastCapture.at > CAPTURE_TTL_MS) {
110
+ lastCapture = void 0;
111
+ return void 0;
112
+ }
113
+ const { error } = lastCapture;
114
+ lastCapture = void 0;
115
+ return error;
116
+ };
117
+ return {
118
+ name: "dev-ssr-error-logger",
119
+ apply: "serve",
120
+ configureServer(server) {
121
+ globalThis[SSR_CAPTURE_KEY] = capture;
122
+ const g = globalThis;
123
+ if (typeof g.addEventListener === "function") {
124
+ const addListener = g.addEventListener;
125
+ addListener("error", (e) => capture(e.error ?? e));
126
+ addListener("unhandledrejection", (e) => capture(e.reason));
127
+ }
128
+ const onUnhandledRejection = (reason) => capture(reason);
129
+ process.on("unhandledRejection", onUnhandledRejection);
130
+ server.httpServer?.once("close", () => {
131
+ process.off("unhandledRejection", onUnhandledRejection);
132
+ });
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
+ }
146
+ 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
+ });
152
+ } catch {
153
+ }
154
+ }
155
+ return origEnd(...args);
156
+ };
157
+ next();
158
+ });
159
+ },
160
+ transform(code, id) {
161
+ const normalizedId = id.replace(/\\/g, "/");
162
+ const isTargetModule = normalizedId.includes("/@tanstack/start-server-core/src/request-response.ts") || normalizedId.includes("/@tanstack/start-server-core/dist/esm/request-response.js");
163
+ if (!isTargetModule) {
164
+ return null;
165
+ }
166
+ const needle = "handler(request, requestOpts)";
167
+ if (!code.includes(needle)) {
168
+ return null;
169
+ }
170
+ return code.replace(
171
+ needle,
172
+ `Promise.resolve(${needle}).catch((err) => { globalThis.${SSR_CAPTURE_KEY}?.(err); throw err; })`
173
+ );
174
+ }
175
+ };
176
+ }
99
177
  function devServerFnErrorLogger() {
100
178
  const HMR_SEND_KEY = "__TANSTACK_SERVER_FN_HMR_SEND__";
101
179
  return {
@@ -154,7 +232,7 @@ function defineConfig(configOrOptions = {}) {
154
232
  options = { vite: await configOrOptions };
155
233
  } else {
156
234
  const optionObject = configOrOptions && typeof configOrOptions === "object" ? configOrOptions : {};
157
- const hasLovableKey = "vite" in optionObject || "serverFnErrorLogger" in optionObject || "cloudflare" in optionObject || "tanstackStart" in optionObject || "react" in optionObject || "envDefine" in optionObject || "hmrGate" in optionObject;
235
+ const hasLovableKey = "vite" in optionObject || "serverFnErrorLogger" in optionObject || "ssrErrorLogger" in optionObject || "cloudflare" in optionObject || "tanstackStart" in optionObject || "react" in optionObject || "envDefine" in optionObject || "hmrGate" in optionObject;
158
236
  options = hasLovableKey ? optionObject : { vite: optionObject };
159
237
  }
160
238
  const internalPlugins = [];
@@ -165,6 +243,9 @@ function defineConfig(configOrOptions = {}) {
165
243
  if (options.serverFnErrorLogger !== false) {
166
244
  internalPlugins.push(devServerFnErrorLogger());
167
245
  }
246
+ if (options.ssrErrorLogger !== false) {
247
+ internalPlugins.push(devSsrErrorLogger());
248
+ }
168
249
  if (options.cloudflare !== false && command === "build") {
169
250
  try {
170
251
  const { cloudflare } = await import("@cloudflare/vite-plugin");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lovable.dev/vite-tanstack-config",
3
- "version": "1.5.1",
3
+ "version": "1.7.0",
4
4
  "description": "Vite config wrapper for Lovable TanStack Start projects",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",