Repository navigation
Tanstack Start build size #8191
|
I've been trying to build an app using Tanstack Start, Powersync and publish it to Cloudflare Workers. However, I'm running into issues with the resulting build size being too big to release to CF. After looking at the build output, the culprit seems to be that the server build includes a bunch of wasm files that are required by the Powersync on the client. The source of these wasm files is the NPM package Now these wasm files are not required on the server, to my knowledge, since they are a dependency of Powersync which is intended to run on the client only (note, the wasm files are in the client and server build output). I've tried a number of different options to elimintate these wasm files from the server output but have failed. I'm not sure which component is the best place to start with this issue. I'm just assuming that the compiler is the best place to start. To repro this issue, I grabbed a Powersync community maintained example and followed the Cloudflare deployment docs. Here is the result on my fork of the example. If you build this, you should find that the server build contains all of the wasm files as discussed above.
Any thoughts on where I'm going wrong? |
Replies: 4 comments 1 reply
|
The issue stems from how Vite / Vinxi traces and bundles imports during the SSR build pass ( Even if your code checks To keep the server worker lightweight, here are the most effective ways to resolve it: 1. Dynamic import on the client (Recommended)Avoid importing PowerSync or initializing the database at module scope in route files. Instead, load PowerSync lazily inside a client hook or import { useState, useEffect } from 'react';
import type { PowerSyncDatabase } from '@powersync/web';
let dbInstance: PowerSyncDatabase | null = null;
export function useClientPowerSync() {
const [db, setDb] = useState<PowerSyncDatabase | null>(dbInstance);
useEffect(() => {
if (dbInstance) return;
let isMounted = true;
// Dynamic import ensures Vite excludes wa-sqlite and wasm from the SSR bundle
import('./powersync-setup').then(async ({ initPowerSync }) => {
const instance = await initPowerSync();
dbInstance = instance;
if (isMounted) setDb(instance);
});
return () => { isMounted = false; };
}, []);
return db;
}Because 2. Externalize in
|
|
Hi @DucMinhNe, Thanks for your detailed response, you definitely helped with my understanding of the situation. However, I'm having trouble implementing your solution. I've updated my repro with your changes and tried to remove all other code to isolate the problem. I added a GH Workflow to demonstrate the build issue, see the latest run here. The output from the run:
Regarding externalizing the dependencies, I had to adjust the code you suggested to work with the repro codebase: export default defineConfig({
...
ssr: {
external: ["@journeyapps/wa-sqlite", "@powersync/web"],
}
});But when I build this, I get an error specifically saying that this is not compatible with Cloudflare Vite plugin: The last suggestion builds, but doesn't seem to make a difference to the output at the moment. It might be more useful once other steps are working. Your |
|
I was able to reproduce this with the current example. The important detail is that this is not fixed by With "rules": [{ "type": "ESModule", "globs": ["**/*.js", "**/*.mjs"] }],
"no_bundle": trueThat makes Wrangler scan every JS module under A practical workaround is a post-build cleanup step that keeps
The generated broad rule can remain when the orphan files are gone. I would not remove the rule entirely: with |
|
@barry166 thanks for the detailed breakdown and actually running the repro at your end. I had a look at I also had AI generate a script to recursively walk through the imports and output a visual. It is helpful to see the changes also. I guess there might be existing tools that can be used to achieve the same thing. I should raise an issue with Cloudflare to see if they can fix the underlying issue and in the meantime I guess I'll write a post-build script to patch the output as suggested. |


I was able to reproduce this with the current example. The important detail is that this is not fixed by
ClientOnlyor by moving the call intouseEffect: those change rendering/runtime, but Vite still emits the client-only dynamic-import branch during the SSR build.With
@cloudflare/vite-plugin@1.52.1, Vite7.3.2, and Wrangler4.123.0, the generated server output has no PowerSync entries indist/server/.vite/manifest.json, and the server chunks do not reference PowerSync. Nevertheless, the Cloudflare-generated config uses:That makes Wrangler scan every JS module under
dist/server, including the orphan …