4

I am trying to bundle my Node.js server-side code (Koa app) into a single file which is what Webpack produces when I use the target as node. This is what ncc - node.js compiler collection also achieves.

I am now using Vite for my next project. I am able to bundle the code by using the SSR bundling feature provided by Vite. But I am not able to bundle third-party dependencies into this single file excluding core/built-in node.js modules. This is my Vite build script:

import path from 'path';
import { build } from 'vite';
import builtinModules from 'builtin-modules';

async function main() {

  const result = await build({
    mode: 'production',
    appType: 'custom',
    root: path.join(process.cwd(), 'backend'),
    ssr: {
      format: 'esm',
      target: 'node',
      
      // By default Vite bundles everything except the one we pass via `external` array.
      external: builtinModules
    },
    build: {
      manifest: false,
      rollupOptions: {
        input: 'backend/main.mjs',
        output: {
          assetFileNames: 'bundle.js'
        }
      },
      outDir: '../dist/backend',
      ssr: true,
      emptyOutDir: true
    },
    plugins: [],
  });
}

main();

In the above code, builtinModules is simply the string array of all the core node.js modules:

// builtinModules
[
  'assert',         'async_hooks',
  'buffer',         'child_process',
  'cluster',        'console',
  'constants',      'crypto',
  'dgram',          'diagnostics_channel',
  'dns',            'domain',
  'events',         'fs',
  'http',           'http2',
  'https',          'inspector',
  'module',         'net',
  'os',             'path',
  'perf_hooks',     'process',
  'punycode',       'querystring',
  'readline',       'repl',
  'stream',         'string_decoder',
  'timers',         'tls',
  'trace_events',   'tty',
  'url',            'util',
  'v8',             'vm',
  'worker_threads', 'zlib'
] 

For my original source code:

// main.mjs
import path from 'path';

import Koa from 'koa';

async function main() {
  console.log('Source folder', path.join(__dirname, 'src'));

  const app = new Koa();

  app.listen(3000);
}

main();

The produced output for the above Vite build configuration is:

// bundle.js
import path from "path";
import Koa from "koa";
async function main() {
  console.log("Source folder", path.join(__dirname, "src"));
  const app = new Koa();
  app.listen(3e3);
}
main();

Ideally, I expected that it would bundle the koa package in the final output leaving behind only native path module. But that's not the case.

So, how do I enable bundling of my entire backend/Node.js source code compile into single file leaving only core node.js modules as external as Node.js is the runtime for my code? The vite server options also provide the noexternal option but setting it to the true works only for webworker like runtime where none of the built-in node modules are available.

Harshal Patil
  • 17,838
  • 14
  • 60
  • 126
  • Out of interest: I can understand wanting to bundle FE code, but what's the rationale behind wanting to bundle BE code? – Andy Feb 07 '23 at 10:47
  • In the end nodejs server side code is always in memory, unless you specify so. I'm also not sure why you want to bundle it in one file. – Leroy Feb 07 '23 at 10:52
  • 3
    @Andy Very valid question. Bundling server-code is extremely useful for serverless deployments. With this, the cold starts are avoided and `npm install` is not required when it gets deployed. The output of the CI/CD is just one file that is ready to be deployed anywhere the Node.js runtime is supported. – Harshal Patil Feb 07 '23 at 10:52
  • Maybe [https://github.com/axe-me/vite-plugin-node](https://github.com/axe-me/vite-plugin-node) based off [this question](https://stackoverflow.com/questions/69523560/using-vite-for-backend). Looks like it was updated a month ago. @HarshalPatil – Andy Feb 07 '23 at 11:05
  • The `vite-plugin-node` works and compiles successfully but doesn't achieve the required single file bundling. I guess I will have to rollout something on my own. – Harshal Patil Feb 07 '23 at 11:21

2 Answers2

0

Latest vite versions are using preloading you should tweak some rollup options to disable code splitting

import { defineConfig } from 'vite' // 2.8.0
import react from '@vitejs/plugin-react' // 1.0.7

export default defineConfig ({
  plugins: [react()],
  build: {
    rollupOptions: {
      output: {
        manualChunks: {}
      },
    },
  },
})
sancelot
  • 1,905
  • 12
  • 31
0

This answer from @svedova seems to work: https://github.com/vitejs/vite/issues/7821#issuecomment-1382317338

vite.config.ts

import { defineConfig } from "vite";

import packageJson from "./package.json";

const dependencies = Object.keys({
  ...packageJson.dependencies,
  ...packageJson.devDependencies,
});

const noExternal = process.env.NODE_ENV === "production" ? dependencies : []

export default defineConfig({
  build: {
    ssr: "./src/server-entry.ts",
    outDir: "./dist",
  },
  ssr: {
    noExternal
  },
});
user2025261
  • 155
  • 7