diff --git a/.changeset/big-trainers-love.md b/.changeset/big-trainers-love.md
new file mode 100644
index 000000000000..166f2fc7f91b
--- /dev/null
+++ b/.changeset/big-trainers-love.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/adapter-node': patch
+---
+
+Bundle non-production dependencies with esbuild
diff --git a/packages/adapter-node/README.md b/packages/adapter-node/README.md
index a7e7ed52bbdb..80dc3d2a98d7 100644
--- a/packages/adapter-node/README.md
+++ b/packages/adapter-node/README.md
@@ -140,12 +140,14 @@ app.listen(3000, () => {
 
 ## Deploying
 
-You will need the output directory (`build` by default), the project's `package.json`, and the production dependencies in `node_modules` to run the application. Production dependencies can be generated with `npm ci --prod`, you can also skip this step if your app doesn't have any dependencies. You can then start your app with
+You will need the output directory (`build` by default), the project's `package.json`, and the production dependencies in `node_modules` to run the application. Production dependencies can be generated with `npm ci --prod` (you can skip this step if your app doesn't have any dependencies). You can then start your app with
 
 ```bash
 node build
 ```
 
+Development dependencies will be bundled into your app using `esbuild`. To control whether a given package is bundled or externalised, place it in `devDependencies` or `dependencies` respectively in your `package.json`.
+
 ## Changelog
 
 [The Changelog for this package is available on GitHub](https://github.com/sveltejs/kit/blob/master/packages/adapter-node/CHANGELOG.md).
diff --git a/packages/adapter-node/ambient.d.ts b/packages/adapter-node/ambient.d.ts
index 537f7cc041d1..15d55418239b 100644
--- a/packages/adapter-node/ambient.d.ts
+++ b/packages/adapter-node/ambient.d.ts
@@ -1,8 +1,16 @@
-declare module 'SERVER' {
-	export { Server } from '@sveltejs/kit';
+declare module 'ENV' {
+	export function env(key: string, fallback?: any): string;
+}
+
+declare module 'HANDLER' {
+	export const handler: import('polka').Middleware;
 }
 
 declare module 'MANIFEST' {
 	import { SSRManifest } from '@sveltejs/kit';
 	export const manifest: SSRManifest;
 }
+
+declare module 'SERVER' {
+	export { Server } from '@sveltejs/kit';
+}
diff --git a/packages/adapter-node/index.js b/packages/adapter-node/index.js
index ef6e8adab00b..48a0ac5c38a8 100644
--- a/packages/adapter-node/index.js
+++ b/packages/adapter-node/index.js
@@ -1,5 +1,6 @@
-import { writeFileSync } from 'fs';
+import { readFileSync, writeFileSync } from 'fs';
 import { fileURLToPath } from 'url';
+import * as esbuild from 'esbuild';
 
 const files = fileURLToPath(new URL('./files', import.meta.url).href);
 
@@ -19,33 +20,54 @@ export default function (opts = {}) {
 		name: '@sveltejs/adapter-node',
 
 		async adapt(builder) {
+			const tmp = builder.getBuildDirectory('adapter-node');
+
 			builder.rimraf(out);
+			builder.rimraf(tmp);
+			builder.mkdirp(tmp);
 
 			builder.log.minor('Copying assets');
 			builder.writeClient(`${out}/client`);
-			builder.writeServer(`${out}/server`);
 			builder.writePrerendered(`${out}/prerendered`);
 
+			if (precompress) {
+				builder.log.minor('Compressing assets');
+				await builder.compress(`${out}/client`);
+				await builder.compress(`${out}/prerendered`);
+			}
+
+			builder.log.minor('Building server');
+
+			builder.writeServer(tmp);
+
 			writeFileSync(
-				`${out}/manifest.js`,
-				`export const manifest = ${builder.generateManifest({
-					relativePath: './server'
-				})};\n`
+				`${tmp}/manifest.js`,
+				`export const manifest = ${builder.generateManifest({ relativePath: './' })};`
 			);
 
+			const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
+
+			await esbuild.build({
+				platform: 'node',
+				sourcemap: 'linked',
+				target: 'es2022',
+				entryPoints: [`${tmp}/index.js`, `${tmp}/manifest.js`],
+				outdir: `${out}/server`,
+				splitting: true,
+				format: 'esm',
+				bundle: true,
+				external: [...Object.keys(pkg.dependencies || {})]
+			});
+
 			builder.copy(files, out, {
 				replace: {
-					SERVER: './server/index.js',
-					MANIFEST: './manifest.js',
+					ENV: './env.js',
+					HANDLER: './handler.js',
+					MANIFEST: './server/manifest.js',
+					SERVER: `./server/index.js`,
 					ENV_PREFIX: JSON.stringify(envPrefix)
 				}
 			});
-
-			if (precompress) {
-				builder.log.minor('Compressing assets');
-				await builder.compress(`${out}/client`);
-				await builder.compress(`${out}/prerendered`);
-			}
 		}
 	};
 }
diff --git a/packages/adapter-node/package.json b/packages/adapter-node/package.json
index 9e563907553a..86cf0cf1e103 100644
--- a/packages/adapter-node/package.json
+++ b/packages/adapter-node/package.json
@@ -43,5 +43,8 @@
 		"sirv": "^2.0.2",
 		"typescript": "^4.7.4",
 		"uvu": "^0.5.3"
+	},
+	"dependencies": {
+		"esbuild": "^0.14.48"
 	}
 }
diff --git a/packages/adapter-node/rollup.config.js b/packages/adapter-node/rollup.config.js
index 9bd5c9970fab..6384234b220a 100644
--- a/packages/adapter-node/rollup.config.js
+++ b/packages/adapter-node/rollup.config.js
@@ -1,19 +1,35 @@
 import { nodeResolve } from '@rollup/plugin-node-resolve';
 import commonjs from '@rollup/plugin-commonjs';
 import json from '@rollup/plugin-json';
+import { builtinModules } from 'module';
 
 export default [
 	{
-		input: {
-			index: 'src/index.js',
-			handler: 'src/handler.js',
-			shims: 'src/shims.js'
+		input: 'src/index.js',
+		output: {
+			file: 'files/index.js',
+			format: 'esm'
 		},
+		plugins: [nodeResolve(), commonjs(), json()],
+		external: ['ENV', 'HANDLER', ...builtinModules]
+	},
+	{
+		input: 'src/env.js',
 		output: {
-			dir: 'files',
+			file: 'files/env.js',
 			format: 'esm'
 		},
 		plugins: [nodeResolve(), commonjs(), json()],
-		external: ['SERVER', 'MANIFEST', ...require('module').builtinModules]
+		external: ['HANDLER', ...builtinModules]
+	},
+	{
+		input: 'src/handler.js',
+		output: {
+			file: 'files/handler.js',
+			format: 'esm',
+			inlineDynamicImports: true
+		},
+		plugins: [nodeResolve(), commonjs(), json()],
+		external: ['ENV', 'MANIFEST', 'SERVER', ...builtinModules]
 	}
 ];
diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js
index d90bce52d996..b0951fccdc30 100644
--- a/packages/adapter-node/src/handler.js
+++ b/packages/adapter-node/src/handler.js
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'url';
 import { getRequest, setResponse } from '@sveltejs/kit/node';
 import { Server } from 'SERVER';
 import { manifest } from 'MANIFEST';
-import { env } from './env.js';
+import { env } from 'ENV';
 
 /* global ENV_PREFIX */
 
@@ -19,7 +19,7 @@ const address_header = env('ADDRESS_HEADER', '').toLowerCase();
 const protocol_header = env('PROTOCOL_HEADER', '').toLowerCase();
 const host_header = env('HOST_HEADER', 'host').toLowerCase();
 
-const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const dir = path.dirname(fileURLToPath(import.meta.url));
 
 /**
  * @param {string} path
@@ -132,9 +132,9 @@ function get_origin(headers) {
 
 export const handler = sequence(
 	[
-		serve(path.join(__dirname, '/client'), true),
-		serve(path.join(__dirname, '/static')),
-		serve(path.join(__dirname, '/prerendered')),
+		serve(path.join(dir, 'client'), true),
+		serve(path.join(dir, 'static')),
+		serve(path.join(dir, 'prerendered')),
 		ssr
 	].filter(Boolean)
 );
diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js
index 5832b244a51e..ea3feee05860 100644
--- a/packages/adapter-node/src/index.js
+++ b/packages/adapter-node/src/index.js
@@ -1,5 +1,5 @@
-import { handler } from './handler.js';
-import { env } from './env.js';
+import { handler } from 'HANDLER';
+import { env } from 'ENV';
 import polka from 'polka';
 
 export const path = env('SOCKET_PATH', false);
diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts
index f5033d642bea..a4b9cb024db4 100644
--- a/packages/kit/types/index.d.ts
+++ b/packages/kit/types/index.d.ts
@@ -99,7 +99,7 @@ export interface Builder {
 	/**
 	 * @param {string} directory Path to the directory containing the files to be compressed
 	 */
-	compress(directory: string): void;
+	compress(directory: string): Promise<void>;
 }
 
 export interface Config {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0967ad4444da..da92634152ea 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -120,6 +120,7 @@ importers:
       '@sveltejs/kit': workspace:*
       '@types/node': ^16.11.36
       c8: ^7.11.3
+      esbuild: ^0.14.48
       node-fetch: ^3.2.4
       polka: ^1.0.0-next.22
       rimraf: ^3.0.2
@@ -127,6 +128,8 @@ importers:
       sirv: ^2.0.2
       typescript: ^4.7.4
       uvu: ^0.5.3
+    dependencies:
+      esbuild: 0.14.54
     devDependencies:
       '@rollup/plugin-json': 4.1.0_rollup@2.75.7
       '@sveltejs/kit': link:../kit