-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Including server dependencies in the production build #24
Comments
Hi, Installing and building on one machine and copying used modules and build artifacts into a different machine (or container) isn't even supposed to work. Machines have to have the exact same Node version and OS. For example native modules you install on Windows will not work on Linux if you copy them over. My recommendation would be to run the build inside the container itself. If, for some reason, you don't want to do that, you can hand roll a manual solution like the one you describe. In any case, it would be out of scope for vavite and it would have to be handled at Vite level. One trick I used to use was this:
This is still susceptible to Node version and native module mismatches though, so I don't do it anymore. |
@cyco130 Hello, thank you for your insights. At this point I'm convinced that a manual solution is the way to go. To be clear I'm using GitHub actions to build the project then I copy the files to a docker container then I push it to the cloud, both environments are running Linux, so I never faced a compatibility issue. Building on the container itself would be much slower, the approach I'm taking saves a lot of time, but copying My vision for an ultimate Vite setup is being able to have a truly standalone server build, just like how it used to be with Webpack, but most likely it still too early for that. |
The issue for me here is that I don’t mind doing the build on the machine/container that will run the code, but if possible I’d like to do the build and bundle it if possible to remove unnecessary code/libs that aren’t actually needed in the container to reduce the overall size of the container. It’s fine if that’s outside the scope of vavite but that’s where I’m coming from when looking for existing issues on this topic. |
I have a possible solution for this, based on |
👋 , I've been trying to achieve a similar 'distributable build' using Vavite and brillout/vite-plugin-ssr. Happy to share more context, but the TL;DR; is a lightweight PaaS that can run pre-build apps from a private registry, and route public URLs to them based on a configuration editable in a CMS. The goal of which to allow easy creation and deployment of 'micro apps' in our marketplace without developers needing to understand docker, underlying infra, or wiring up anything more than an npm package. We ran into an issue similar to @iMrDJAi 's , related to the auto-import feature of vite-plugin-ssr expecting the build time env to exist 'as-is' at runtime. . I initially got around it by replacing the relative path with a runtime path provided by node's resolve module, however ran into issues when multiple apps were run on the same node instance since it used global variables to register userland code. I didnt have much luck trying to get Vite to bundle the server for me, but I was successful in using vercel/ncc out of the box to re-bundle the server after vite was done with it, which spit out a set of static files similar to Next's standalone feature. I plan to make a vite plugin to handle all this nicely, but as a proof of concept running |
I haven't built a ready-to-use package yet but I have a small script that can do the trick. Assuming your server entry is in import { nodeFileTrace } from "@vercel/nft";
import { spawn } from "node:child_process";
import { mkdir } from "node:fs/promises";
const result = await nodeFileTrace(["./dist/server/index.js"], {});
const files = [...result.fileList];
const out = "/out/";
await mkdir(out, { recursive: true });
// spawn("rsync", ["-Rrl", "dist/client", ...files, out], {
spawn("cp", ["-ar", "--parents", "dist/client", ...files, out], {
stdio: "inherit",
}).on("exit", (code) => {
process.exit(code);
});
This will probably fail in a monorepo where some of the files are outside of the current root but one can accommodate for that with a little bit of effort. I will close this issue because I think this is out of scope for vavite but if anyone creates a ready-to-use solution on top of this idea, I'd love to hear about it. |
This looks like it doesn't do all the niceties of actual bundling, right? Like dead code elimination, tree-shaking etc. to reduce the size. FWIW, I think I've almost got real bundling working using a pure Vite method, by just adding an additional export default defineConfig(async (env: ConfigEnv) => ({
plugins: [
// Work around https://github.com/vikejs/vike/issues/1163
{
name: "no-external-vike",
config(config, _env) {
if (env.ssrBuild) config.ssr.external = [];
},
},
],
ssr: env.ssrBuild
? {
noExternal: [/^(?!(process))/],
}
: {},
build: {
rollupOptions: {
input: env.ssrBuild
? {
index: "server/index_prod.ts",
}
: {},
},
},
}));
|
The generated production build lacks the external dependencies defined on
package.json
. As a result, we can't just copy the/dist
directory into a container and expecting it to work. While in the development phase the server could just use the packages from the/node_modules
directory that is located in the project root, it has to be included inside/dist/server
when the/dist
directory is standalone.Copying the
/node_modules
manually after the build step may work, but it includes packages that aren't necessarily referenced in the server code (e.g. dev dependencies, dependencies used in the client code), so we're increasing the build size for no reason.I have tried including those packages inside Vite's
ssr.noExternal
array, however, Vite isn't capable yet of transpiling all node packages, for examplenode-canvas
that includes a native node module (.node
binary) requires a proper loader that Vite lacks, let alone the increased build time that is caused by processing tons of files!So, my proposal is the following:
1- Listing all dependencies used in the server code.
2- Writing them inside
/dist/server/package.json
.3- Performing
npm install
right after the building process ends.For the first step, I've tried writing a simple plugin to list all imported dependencies:
But it only lists packages imported from the client code. I assume that the server build process is isolated? Thoughts?
The text was updated successfully, but these errors were encountered: