Repository: sveltejs/cli (ts)

Issue:
Run install scripts in `pnpm@10`
In [`pnpm@10`](https://github.com/pnpm/pnpm/releases/tag/v10.0.0), install scripts must be individually enabled.

> Lifecycle scripts of dependencies are not executed during installation by default!

If not, the following message will be logged.

> The following dependencies have build scripts that were ignored: @sveltejs/kit, esbuild

> To allow the execution of build scripts for these packages, add their names to "pnpm.onlyBuiltDependencies" in your "package.json", then run "pnpm rebuild"

What if this field is added to the `package.json` if the user selects pnpm?

```
◇  Which package manager do you want to install dependencies with?
│  pnpm
```

```json
{
  "pnpm": {
    "onlyBuiltDependencies": [
      "@sveltejs/kit",
      "esbuild"
    ]
  }
}
```
`sv create with lucia better-sqlite3`. App errors with 'Error: Could not locate the bindings file.
Hi,
i believe this should be working.
I hope not to be silly and bothering you with stupid mistakes.

But,i am on macos, and here is the problem i report 

```bash
# myenv
node -v #v22.11.0
pnpm -v # 10.0.0
sqlite3 -version # 3.48.0 2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010 (64-bit)

pnpm dlx sv create app1
#  Welcome to the Svelte CLI! (v0.6.16)
# │  SvelteKit minimal
# │  Yes, using Typescript syntax
# │  tailwindcss, sveltekit-adapter, drizzle, lucia
# ◇  sveltekit-adapter: node
# ◇  drizzle: Which database would you like to use?: SQLite
# ◇  drizzle: Which SQLite client would you like to use?: better-sqlite3
# ◇  lucia: Do you want to include a demo?:  Yes
# ◇  tailwindcss: Which plugins would you like to add?

cd app1
git init && git add -A && git commit -m "Initial commit"

pnpm run dev --open
pnpm run db:push # get same error

# > app1@0.0.1 db:push /Users/fb/Documents/Github/mylucia/app1
# > drizzle-kit push

# No config path provided, using default 'drizzle.config.ts'
# Reading config file '/Users/fb/Documents/Github/mylucia/app1/drizzle.config.ts'
# Error: Could not locate the bindings file. Tried:
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/build/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/build/Debug/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/build/Release/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/out/Debug/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/Debug/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/out/Release/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/Release/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/build/default/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/compiled/22.11.0/darwin/arm64/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/addon-build/release/install-root/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/addon-build/debug/install-root/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/addon-build/default/install-root/better_sqlite3.node
#  → /Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/lib/binding/node-v127-darwin-arm64/better_sqlite3.node
#     at bindings (/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/bindings@1.5.0/node_modules/bindings/bindings.js:126:9)
#     at new Database (/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/lib/database.js:48:64)
#     at connectToSQLite (/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/drizzle-kit@0.30.2/node_modules/drizzle-kit/bin.cjs:64920:24)
#     at async sqlitePush (/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/drizzle-kit@0.30.2/node_modules/drizzle-kit/bin.cjs:67969:18)
#     at async Object.handler (/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/drizzle-kit@0.30.2/node_modules/drizzle-kit/bin.cjs:76977:9)
#     at async run (/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/drizzle-kit@0.30.2/node_modules/drizzle-kit/bin.cjs:76221:7) {
#   tries: [
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/build/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/build/Debug/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/build/Release/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/out/Debug/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/Debug/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/out/Release/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/Release/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/build/default/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/compiled/22.11.0/darwin/arm64/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/addon-build/release/install-root/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/addon-build/debug/install-root/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/addon-build/default/install-root/better_sqlite3.node',
#     '/Users/fb/Documents/Github/mylucia/app1/node_modules/.pnpm/better-sqlite3@11.8.1/node_modules/better-sqlite3/lib/binding/node-v127-darwin-arm64/better_sqlite3.node'
#   ]
# }

pnpm list
# dependencies:
# @node-rs/argon2 2.0.2
# @oslojs/crypto 1.0.1
# @oslojs/encoding 1.1.0
# better-sqlite3 11.8.1
# drizzle-orm 0.38.4

# devDependencies:
# @sveltejs/adapter-node 5.2.12       autoprefixer 10.4.20                tailwindcss 3.4.17
# @sveltejs/kit 2.16.1                drizzle-kit 0.30.2                  typescript 5.7.3
# @sveltejs/vite-plugin-svelte 5.0.3  svelte 5.19.3                       vite 6.0.11
# @types/better-sqlite3 7.6.12        svelte-check 4.1.4


```

Pull request:
feat: support `pnpm` version `10`
closes #424 
closes #388 

* updates monorepo to `pnpm@10` to be able to reproduce the error described in the issues above
* adds `esbuild` to `onlyBuiltDependencies` when the users selects `pnpm` during project initialization. This not only applies to project initialization, but to all projects that are using `pnpm` (even existing ones, as soon as you add another add-on to your project)
* exposes the new `sv.pnpmBuildDependendency` api, so that add-ons like `drizzle` (for `better-sqlite3`) can provide further packages that should be added to that list.

Patch:
diff --git a/.changeset/fast-panthers-smile.md b/.changeset/fast-panthers-smile.md
new file mode 100644
index 0000000..a9e1376
--- /dev/null
+++ b/.changeset/fast-panthers-smile.md
@@ -0,0 +1,5 @@
+---
+'sv': patch
+---
+
+feat: support `pnpm` version `10`
diff --git a/documentation/docs/20-commands/30-sv-check.md b/documentation/docs/20-commands/30-sv-check.md
index 4ad025e..52e1a37 100644
--- a/documentation/docs/20-commands/30-sv-check.md
+++ b/documentation/docs/20-commands/30-sv-check.md
@@ -121,7 +121,7 @@ The first row is of type `START` and contains the workspace folder (wrapped in q
 1590680325583 START "/home/user/language-tools/packages/language-server/test/plugins/typescript/testfiles"
 ```
 
-Any number of `ERROR` or `WARNING` records may follow. Their structure is identical and depends on the output argoument.
+Any number of `ERROR` or `WARNING` records may follow. Their structure is identical and depends on the output argument.
 
 If the argument is `machine` it will tell us the filename, the starting line and column numbers, and the error message. The filename is relative to the workspace directory. The filename and the message are both wrapped in quotes. Example:
 
diff --git a/package.json b/package.json
index c96a425..1dab2e7 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
 	"type": "module",
 	"description": "monorepo for sv and friends",
 	"engines": {
-		"pnpm": "^9.0.0"
+		"pnpm": "^10.0.0"
 	},
 	"scripts": {
 		"build": "rolldown --config",
@@ -39,5 +39,5 @@
 		"unplugin-isolated-decl": "^0.8.3",
 		"vitest": "^3.0.3"
 	},
-	"packageManager": "pnpm@9.7.0"
+	"packageManager": "pnpm@10.1.0"
 }
diff --git a/packages/addons/drizzle/index.ts b/packages/addons/drizzle/index.ts
index 698cdc1..0cf8fa6 100644
--- a/packages/addons/drizzle/index.ts
+++ b/packages/addons/drizzle/index.ts
@@ -89,6 +89,7 @@ export default defineAddon({
 		if (options.sqlite === 'better-sqlite3') {
 			sv.dependency('better-sqlite3', '^11.8.0');
 			sv.devDependency('@types/better-sqlite3', '^7.6.12');
+			sv.pnpmBuildDependendency('better-sqlite3');
 		}
 
 		if (options.sqlite === 'libsql' || options.sqlite === 'turso')
diff --git a/packages/cli/commands/add/index.ts b/packages/cli/commands/add/index.ts
index 32014c3..054b47b 100644
--- a/packages/cli/commands/add/index.ts
+++ b/packages/cli/commands/add/index.ts
@@ -18,7 +18,11 @@ import * as common from '../../utils/common.ts';
 import { createWorkspace } from './workspace.ts';
 import { formatFiles, getHighlighter } from './utils.ts';
 import { Directive, downloadPackage, getPackageJSON } from './fetch-packages.ts';
-import { installDependencies, packageManagerPrompt } from '../../utils/package-manager.ts';
+import {
+	addPnpmBuildDependendencies,
+	installDependencies,
+	packageManagerPrompt
+} from '../../utils/package-manager.ts';
 import { getGlobalPreconditions } from './preconditions.ts';
 import { type AddonMap, applyAddons, setupAddons } from '../../lib/install.ts';
 
@@ -425,13 +429,6 @@ export async function runAddCommand(
 	// indicating that installing deps was skipped and no PM was selected
 	if (selectedAddons.length === 0) return { packageManager: null };
 
-	// prompt for package manager
-	let packageManager: PackageManager | undefined;
-	if (options.install) {
-		packageManager = await packageManagerPrompt(options.cwd);
-		if (packageManager) workspace.packageManager = packageManager;
-	}
-
 	// apply addons
 	const officialDetails = Object.keys(official).map((id) => getAddonDetails(id));
 	const commDetails = Object.keys(community).map(
@@ -440,7 +437,7 @@ export async function runAddCommand(
 	const details = officialDetails.concat(commDetails);
 
 	const addonMap: AddonMap = Object.assign({}, ...details.map((a) => ({ [a.id]: a })));
-	const filesToFormat = await applyAddons({
+	const { filesToFormat, pnpmBuildDependencies: addonPnpmBuildDependencies } = await applyAddons({
 		workspace,
 		addonSetupResults,
 		addons: addonMap,
@@ -449,9 +446,21 @@ export async function runAddCommand(
 
 	p.log.success('Successfully setup add-ons');
 
-	// install dependencies
-	if (packageManager && options.install) {
-		await installDependencies(packageManager, options.cwd);
+	// prompt for package manager and install dependencies
+	let packageManager: PackageManager | undefined;
+	if (options.install) {
+		packageManager = await packageManagerPrompt(options.cwd);
+
+		if (packageManager) {
+			workspace.packageManager = packageManager;
+
+			addPnpmBuildDependendencies(workspace.cwd, packageManager, [
+				'esbuild',
+				...addonPnpmBuildDependencies
+			]);
+
+			await installDependencies(packageManager, options.cwd);
+		}
 	}
 
 	// format modified/created files with prettier (if available)
diff --git a/packages/cli/commands/create.ts b/packages/cli/commands/create.ts
index d6a8c55..77a5d8c 100644
--- a/packages/cli/commands/create.ts
+++ b/packages/cli/commands/create.ts
@@ -15,6 +15,7 @@ import * as common from '../utils/common.ts';
 import { runAddCommand } from './add/index.ts';
 import { detectSync, resolveCommand, type AgentName } from 'package-manager-detector';
 import {
+	addPnpmBuildDependendencies,
 	getUserAgent,
 	installDependencies,
 	packageManagerPrompt
@@ -164,6 +165,7 @@ async function createProject(cwd: ProjectPath, options: Options) {
 	let addOnNextSteps: string | undefined;
 	const installDeps = async () => {
 		packageManager = await packageManagerPrompt(projectPath);
+		addPnpmBuildDependendencies(projectPath, packageManager, ['esbuild']);
 		if (packageManager) await installDependencies(packageManager, projectPath);
 	};
 
diff --git a/packages/cli/lib/install.ts b/packages/cli/lib/install.ts
index 72d5173..6f98ce4 100644
--- a/packages/cli/lib/install.ts
+++ b/packages/cli/lib/install.ts
@@ -33,7 +33,7 @@ export async function installAddon<Addons extends AddonMap>({
 	cwd,
 	options,
 	packageManager = 'npm'
-}: InstallOptions<Addons>): Promise<string[]> {
+}: InstallOptions<Addons>): Promise<ReturnType<typeof applyAddons>> {
 	const workspace = createWorkspace({ cwd, packageManager });
 	const addonSetupResults = setupAddons(Object.values(addons), workspace);
 
@@ -51,8 +51,12 @@ export async function applyAddons({
 	workspace,
 	addonSetupResults,
 	options
-}: ApplyAddonOptions): Promise<string[]> {
+}: ApplyAddonOptions): Promise<{
+	filesToFormat: string[];
+	pnpmBuildDependencies: string[];
+}> {
 	const filesToFormat = new Set<string>();
+	const allPnpmBuildDependencies: string[] = [];
 
 	const mapped = Object.entries(addons).map(([, addon]) => addon);
 	const ordered = orderAddons(mapped, addonSetupResults);
@@ -60,11 +64,20 @@ export async function applyAddons({
 	for (const addon of ordered) {
 		workspace = createWorkspace({ ...workspace, options: options[addon.id] });
 
-		const files = await runAddon({ workspace, addon, multiple: ordered.length > 1 });
+		const { files, pnpmBuildDependencies } = await runAddon({
+			workspace,
+			addon,
+			multiple: ordered.length > 1
+		});
+
 		files.forEach((f) => filesToFormat.add(f));
+		pnpmBuildDependencies.forEach((s) => allPnpmBuildDependencies.push(s));
 	}
 
-	return Array.from(filesToFormat);
+	return {
+		filesToFormat: Array.from(filesToFormat),
+		pnpmBuildDependencies: allPnpmBuildDependencies
+	};
 }
 
 export function setupAddons(
@@ -91,7 +104,7 @@ type RunAddon = {
 	addon: Addon<Record<string, Question>>;
 	multiple: boolean;
 };
-async function runAddon({ addon, multiple, workspace }: RunAddon): Promise<string[]> {
+async function runAddon({ addon, multiple, workspace }: RunAddon) {
 	const files = new Set<string>();
 
 	// apply default addon options
@@ -103,6 +116,7 @@ async function runAddon({ addon, multiple, workspace }: RunAddon): Promise<strin
 	}
 
 	const dependencies: Array<{ pkg: string; version: string; dev: boolean }> = [];
+	const pnpmBuildDependencies: string[] = [];
 	const sv: SvApi = {
 		file: (path, content) => {
 			try {
@@ -150,6 +164,9 @@ async function runAddon({ addon, multiple, workspace }: RunAddon): Promise<strin
 		},
 		devDependency: (pkg, version) => {
 			dependencies.push({ pkg, version, dev: true });
+		},
+		pnpmBuildDependendency: (pkg) => {
+			pnpmBuildDependencies.push(pkg);
 		}
 	};
 	await addon.run({ ...workspace, sv });
@@ -157,7 +174,10 @@ async function runAddon({ addon, multiple, workspace }: RunAddon): Promise<strin
 	const pkgPath = installPackages(dependencies, workspace);
 	files.add(pkgPath);
 
-	return Array.from(files);
+	return {
+		files: Array.from(files),
+		pnpmBuildDependencies
+	};
 }
 
 // sorts them to their execution order
diff --git a/packages/cli/utils/package-manager.ts b/packages/cli/utils/package-manager.ts
index 0f979fc..64babcb 100644
--- a/packages/cli/utils/package-manager.ts
+++ b/packages/cli/utils/package-manager.ts
@@ -1,4 +1,7 @@
+import fs from 'node:fs';
+import path from 'node:path';
 import process from 'node:process';
+import * as find from 'empathic/find';
 import { exec } from 'tinyexec';
 import * as p from '@sveltejs/clack-prompts';
 import {
@@ -8,6 +11,7 @@ import {
 	detectSync,
 	type AgentName
 } from 'package-manager-detector';
+import { parseJson } from '@sveltejs/cli-core/parsers';
 
 const agents = AGENTS.filter((agent): agent is AgentName => !agent.includes('@'));
 const agentOptions: PackageManagerOptions = agents.map((pm) => ({ value: pm, label: pm }));
@@ -67,3 +71,33 @@ export function getUserAgent(): AgentName | undefined {
 	const name = pmSpec.substring(0, separatorPos) as AgentName;
 	return AGENTS.includes(name) ? name : undefined;
 }
+
+export function addPnpmBuildDependendencies(
+	cwd: string,
+	packageManager: AgentName | null | undefined,
+	allowedPackages: string[]
+) {
+	// other package managers are currently not affected by this change
+	if (!packageManager || packageManager !== 'pnpm') return;
+
+	// find the workspace root
+	const pnpmWorkspacePath = find.up('pnpm-workspace.yaml', { cwd });
+	if (!pnpmWorkspacePath) return;
+
+	// load the package.json
+	const pkgPath = path.join(path.dirname(pnpmWorkspacePath), 'package.json');
+	const content = fs.readFileSync(pkgPath, 'utf-8');
+	const { data, generateCode } = parseJson(content);
+
+	// add the packages where we install scripts should be executed
+	data.pnpm ??= {};
+	data.pnpm.onlyBuiltDependencies ??= [];
+	for (const allowedPackage of allowedPackages) {
+		if (data.pnpm.onlyBuiltDependencies.includes(allowedPackage)) continue;
+		data.pnpm.onlyBuiltDependencies.push(allowedPackage);
+	}
+
+	// save the updated package.json
+	const newContent = generateCode();
+	fs.writeFileSync(pkgPath, newContent);
+}
diff --git a/packages/core/addon/config.ts b/packages/core/addon/config.ts
index 9890046..be36f77 100644
--- a/packages/core/addon/config.ts
+++ b/packages/core/addon/config.ts
@@ -20,10 +20,11 @@ export type Scripts<Args extends OptionDefinition> = {
 };
 
 export type SvApi = {
-	file: (path: string, edit: (content: string) => string) => void;
+	pnpmBuildDependendency: (pkg: string) => void;
 	dependency: (pkg: string, version: string) => void;
 	devDependency: (pkg: string, version: string) => void;
 	execute: (args: string[], stdio: 'inherit' | 'pipe') => Promise<void>;
+	file: (path: string, edit: (content: string) => string) => void;
 };
 
 export type Addon<Args extends OptionDefinition> = {


Test patch:
diff --git a/community-addon-template/tests/setup/suite.ts b/community-addon-template/tests/setup/suite.ts
index 46f3f8c..9b2dc0a 100644
--- a/community-addon-template/tests/setup/suite.ts
+++ b/community-addon-template/tests/setup/suite.ts
@@ -3,7 +3,13 @@ import path from 'node:path';
 import { execSync } from 'node:child_process';
 import * as vitest from 'vitest';
 import { installAddon, type AddonMap, type OptionMap } from 'sv';
-import { createProject, startPreview, type CreateProject, type ProjectVariant } from 'sv/testing';
+import {
+	addPnpmBuildDependendencies,
+	createProject,
+	startPreview,
+	type CreateProject,
+	type ProjectVariant
+} from 'sv/testing';
 import { chromium, type Browser, type Page } from '@playwright/test';
 import { fileURLToPath } from 'node:url';
 
@@ -40,9 +46,18 @@ export function setupTest<Addons extends AddonMap>(addons: Addons) {
 		// creates a pnpm workspace in each addon dir
 		fs.writeFileSync(
 			path.resolve(cwd, testName, 'pnpm-workspace.yaml'),
-			`packages:\n  - '**/*'`,
+			"packages:\n  - '**/*'",
 			'utf8'
 		);
+
+		// creates a barebones package.json in each addon dir
+		fs.writeFileSync(
+			path.resolve(cwd, testName, 'package.json'),
+			JSON.stringify({
+				name: `${testName}-workspace-root`,
+				private: true
+			})
+		);
 	});
 
 	// runs before each test case
@@ -57,7 +72,13 @@ export function setupTest<Addons extends AddonMap>(addons: Addons) {
 			fs.writeFileSync(metaPath, JSON.stringify({ variant, options }, null, '\t'), 'utf8');
 
 			// run addon
-			await installAddon({ cwd, addons, options, packageManager: 'pnpm' });
+			const { pnpmBuildDependencies } = await installAddon({
+				cwd,
+				addons,
+				options,
+				packageManager: 'pnpm'
+			});
+			addPnpmBuildDependendencies(cwd, 'pnpm', ['esbuild', ...pnpmBuildDependencies]);
 
 			return cwd;
 		};
diff --git a/packages/addons/_tests/_setup/suite.ts b/packages/addons/_tests/_setup/suite.ts
index 9af9de8..5c6e5a1 100644
--- a/packages/addons/_tests/_setup/suite.ts
+++ b/packages/addons/_tests/_setup/suite.ts
@@ -3,7 +3,13 @@ import path from 'node:path';
 import { execSync } from 'node:child_process';
 import * as vitest from 'vitest';
 import { installAddon, type AddonMap, type OptionMap } from 'sv';
-import { createProject, startPreview, type CreateProject, type ProjectVariant } from 'sv/testing';
+import {
+	createProject,
+	startPreview,
+	addPnpmBuildDependendencies,
+	type CreateProject,
+	type ProjectVariant
+} from 'sv/testing';
 import { chromium, type Browser, type Page } from '@playwright/test';
 
 const cwd = vitest.inject('testDir');
@@ -40,6 +46,15 @@ export function setupTest<Addons extends AddonMap>(addons: Addons) {
 			"packages:\n  - '**/*'",
 			'utf8'
 		);
+
+		// creates a barebones package.json in each addon dir
+		fs.writeFileSync(
+			path.resolve(cwd, testName, 'package.json'),
+			JSON.stringify({
+				name: `${testName}-workspace-root`,
+				private: true
+			})
+		);
 	});
 
 	// runs before each test case
@@ -54,7 +69,13 @@ export function setupTest<Addons extends AddonMap>(addons: Addons) {
 			fs.writeFileSync(metaPath, JSON.stringify({ variant, options }, null, '\t'), 'utf8');
 
 			// run addon
-			await installAddon({ cwd, addons, options, packageManager: 'pnpm' });
+			const { pnpmBuildDependencies } = await installAddon({
+				cwd,
+				addons,
+				options,
+				packageManager: 'pnpm'
+			});
+			addPnpmBuildDependendencies(cwd, 'pnpm', ['esbuild', ...pnpmBuildDependencies]);
 
 			return cwd;
 		};
diff --git a/packages/cli/lib/testing.ts b/packages/cli/lib/testing.ts
index 6285752..101214b 100644
--- a/packages/cli/lib/testing.ts
+++ b/packages/cli/lib/testing.ts
@@ -6,6 +6,7 @@ import { exec } from 'tinyexec';
 import { create } from '@sveltejs/create';
 import pstree, { type PS } from 'ps-tree';
 
+export { addPnpmBuildDependendencies } from '../utils/package-manager.ts';
 export type ProjectVariant = 'kit-js' | 'kit-ts' | 'vite-js' | 'vite-ts';
 
 const TEMPLATES_DIR = '.templates';