Skip to content

Commit 41cc5f6

Browse files
authored
feat: support pnpm version 10 (#432)
* switch to `pnpm@10` * changeset * allow `esbuild` * allow addons to provide allowed postinstall scripts * fix test? * fix test? * only test better-sqlite * only test better-sqlite * remove silent flag * add postinstall scripts to workspace root * Revert "fix test?" This reverts commit e51f1d0. * Revert "remove silent flag" This reverts commit dd80822. * Revert "only test better-sqlite" This reverts commit 913bbf5. * avoid bad import * cleanup * cleanup * cleanup * fix
1 parent e325bb2 commit 41cc5f6

File tree

11 files changed

+141
-26
lines changed

11 files changed

+141
-26
lines changed

.changeset/fast-panthers-smile.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'sv': patch
3+
---
4+
5+
feat: support `pnpm` version `10`

community-addon-template/tests/setup/suite.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import path from 'node:path';
33
import { execSync } from 'node:child_process';
44
import * as vitest from 'vitest';
55
import { installAddon, type AddonMap, type OptionMap } from 'sv';
6-
import { createProject, startPreview, type CreateProject, type ProjectVariant } from 'sv/testing';
6+
import {
7+
addPnpmBuildDependendencies,
8+
createProject,
9+
startPreview,
10+
type CreateProject,
11+
type ProjectVariant
12+
} from 'sv/testing';
713
import { chromium, type Browser, type Page } from '@playwright/test';
814
import { fileURLToPath } from 'node:url';
915

@@ -40,9 +46,18 @@ export function setupTest<Addons extends AddonMap>(addons: Addons) {
4046
// creates a pnpm workspace in each addon dir
4147
fs.writeFileSync(
4248
path.resolve(cwd, testName, 'pnpm-workspace.yaml'),
43-
`packages:\n - '**/*'`,
49+
"packages:\n - '**/*'",
4450
'utf8'
4551
);
52+
53+
// creates a barebones package.json in each addon dir
54+
fs.writeFileSync(
55+
path.resolve(cwd, testName, 'package.json'),
56+
JSON.stringify({
57+
name: `${testName}-workspace-root`,
58+
private: true
59+
})
60+
);
4661
});
4762

4863
// runs before each test case
@@ -57,7 +72,13 @@ export function setupTest<Addons extends AddonMap>(addons: Addons) {
5772
fs.writeFileSync(metaPath, JSON.stringify({ variant, options }, null, '\t'), 'utf8');
5873

5974
// run addon
60-
await installAddon({ cwd, addons, options, packageManager: 'pnpm' });
75+
const { pnpmBuildDependencies } = await installAddon({
76+
cwd,
77+
addons,
78+
options,
79+
packageManager: 'pnpm'
80+
});
81+
addPnpmBuildDependendencies(cwd, 'pnpm', ['esbuild', ...pnpmBuildDependencies]);
6182

6283
return cwd;
6384
};

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "module",
66
"description": "monorepo for sv and friends",
77
"engines": {
8-
"pnpm": "^9.0.0"
8+
"pnpm": "^10.0.0"
99
},
1010
"scripts": {
1111
"build": "rolldown --config",
@@ -39,5 +39,5 @@
3939
"unplugin-isolated-decl": "^0.8.3",
4040
"vitest": "^3.0.3"
4141
},
42-
"packageManager": "pnpm@9.7.0"
42+
"packageManager": "pnpm@10.1.0"
4343
}

packages/addons/_tests/_setup/suite.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import path from 'node:path';
33
import { execSync } from 'node:child_process';
44
import * as vitest from 'vitest';
55
import { installAddon, type AddonMap, type OptionMap } from 'sv';
6-
import { createProject, startPreview, type CreateProject, type ProjectVariant } from 'sv/testing';
6+
import {
7+
createProject,
8+
startPreview,
9+
addPnpmBuildDependendencies,
10+
type CreateProject,
11+
type ProjectVariant
12+
} from 'sv/testing';
713
import { chromium, type Browser, type Page } from '@playwright/test';
814

915
const cwd = vitest.inject('testDir');
@@ -40,6 +46,15 @@ export function setupTest<Addons extends AddonMap>(addons: Addons) {
4046
"packages:\n - '**/*'",
4147
'utf8'
4248
);
49+
50+
// creates a barebones package.json in each addon dir
51+
fs.writeFileSync(
52+
path.resolve(cwd, testName, 'package.json'),
53+
JSON.stringify({
54+
name: `${testName}-workspace-root`,
55+
private: true
56+
})
57+
);
4358
});
4459

4560
// runs before each test case
@@ -54,7 +69,13 @@ export function setupTest<Addons extends AddonMap>(addons: Addons) {
5469
fs.writeFileSync(metaPath, JSON.stringify({ variant, options }, null, '\t'), 'utf8');
5570

5671
// run addon
57-
await installAddon({ cwd, addons, options, packageManager: 'pnpm' });
72+
const { pnpmBuildDependencies } = await installAddon({
73+
cwd,
74+
addons,
75+
options,
76+
packageManager: 'pnpm'
77+
});
78+
addPnpmBuildDependendencies(cwd, 'pnpm', ['esbuild', ...pnpmBuildDependencies]);
5879

5980
return cwd;
6081
};

packages/addons/drizzle/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export default defineAddon({
8989
if (options.sqlite === 'better-sqlite3') {
9090
sv.dependency('better-sqlite3', '^11.8.0');
9191
sv.devDependency('@types/better-sqlite3', '^7.6.12');
92+
sv.pnpmBuildDependendency('better-sqlite3');
9293
}
9394

9495
if (options.sqlite === 'libsql' || options.sqlite === 'turso')

packages/cli/commands/add/index.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ import * as common from '../../utils/common.ts';
1818
import { createWorkspace } from './workspace.ts';
1919
import { formatFiles, getHighlighter } from './utils.ts';
2020
import { Directive, downloadPackage, getPackageJSON } from './fetch-packages.ts';
21-
import { installDependencies, packageManagerPrompt } from '../../utils/package-manager.ts';
21+
import {
22+
addPnpmBuildDependendencies,
23+
installDependencies,
24+
packageManagerPrompt
25+
} from '../../utils/package-manager.ts';
2226
import { getGlobalPreconditions } from './preconditions.ts';
2327
import { type AddonMap, applyAddons, setupAddons } from '../../lib/install.ts';
2428

@@ -425,13 +429,6 @@ export async function runAddCommand(
425429
// indicating that installing deps was skipped and no PM was selected
426430
if (selectedAddons.length === 0) return { packageManager: null };
427431

428-
// prompt for package manager
429-
let packageManager: PackageManager | undefined;
430-
if (options.install) {
431-
packageManager = await packageManagerPrompt(options.cwd);
432-
if (packageManager) workspace.packageManager = packageManager;
433-
}
434-
435432
// apply addons
436433
const officialDetails = Object.keys(official).map((id) => getAddonDetails(id));
437434
const commDetails = Object.keys(community).map(
@@ -440,7 +437,7 @@ export async function runAddCommand(
440437
const details = officialDetails.concat(commDetails);
441438

442439
const addonMap: AddonMap = Object.assign({}, ...details.map((a) => ({ [a.id]: a })));
443-
const filesToFormat = await applyAddons({
440+
const { filesToFormat, pnpmBuildDependencies: addonPnpmBuildDependencies } = await applyAddons({
444441
workspace,
445442
addonSetupResults,
446443
addons: addonMap,
@@ -449,9 +446,21 @@ export async function runAddCommand(
449446

450447
p.log.success('Successfully setup add-ons');
451448

452-
// install dependencies
453-
if (packageManager && options.install) {
454-
await installDependencies(packageManager, options.cwd);
449+
// prompt for package manager and install dependencies
450+
let packageManager: PackageManager | undefined;
451+
if (options.install) {
452+
packageManager = await packageManagerPrompt(options.cwd);
453+
454+
if (packageManager) {
455+
workspace.packageManager = packageManager;
456+
457+
addPnpmBuildDependendencies(workspace.cwd, packageManager, [
458+
'esbuild',
459+
...addonPnpmBuildDependencies
460+
]);
461+
462+
await installDependencies(packageManager, options.cwd);
463+
}
455464
}
456465

457466
// format modified/created files with prettier (if available)

packages/cli/commands/create.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import * as common from '../utils/common.ts';
1515
import { runAddCommand } from './add/index.ts';
1616
import { detectSync, resolveCommand, type AgentName } from 'package-manager-detector';
1717
import {
18+
addPnpmBuildDependendencies,
1819
getUserAgent,
1920
installDependencies,
2021
packageManagerPrompt
@@ -164,6 +165,7 @@ async function createProject(cwd: ProjectPath, options: Options) {
164165
let addOnNextSteps: string | undefined;
165166
const installDeps = async () => {
166167
packageManager = await packageManagerPrompt(projectPath);
168+
addPnpmBuildDependendencies(projectPath, packageManager, ['esbuild']);
167169
if (packageManager) await installDependencies(packageManager, projectPath);
168170
};
169171

packages/cli/lib/install.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export async function installAddon<Addons extends AddonMap>({
3333
cwd,
3434
options,
3535
packageManager = 'npm'
36-
}: InstallOptions<Addons>): Promise<string[]> {
36+
}: InstallOptions<Addons>): Promise<ReturnType<typeof applyAddons>> {
3737
const workspace = createWorkspace({ cwd, packageManager });
3838
const addonSetupResults = setupAddons(Object.values(addons), workspace);
3939

@@ -51,20 +51,33 @@ export async function applyAddons({
5151
workspace,
5252
addonSetupResults,
5353
options
54-
}: ApplyAddonOptions): Promise<string[]> {
54+
}: ApplyAddonOptions): Promise<{
55+
filesToFormat: string[];
56+
pnpmBuildDependencies: string[];
57+
}> {
5558
const filesToFormat = new Set<string>();
59+
const allPnpmBuildDependencies: string[] = [];
5660

5761
const mapped = Object.entries(addons).map(([, addon]) => addon);
5862
const ordered = orderAddons(mapped, addonSetupResults);
5963

6064
for (const addon of ordered) {
6165
workspace = createWorkspace({ ...workspace, options: options[addon.id] });
6266

63-
const files = await runAddon({ workspace, addon, multiple: ordered.length > 1 });
67+
const { files, pnpmBuildDependencies } = await runAddon({
68+
workspace,
69+
addon,
70+
multiple: ordered.length > 1
71+
});
72+
6473
files.forEach((f) => filesToFormat.add(f));
74+
pnpmBuildDependencies.forEach((s) => allPnpmBuildDependencies.push(s));
6575
}
6676

67-
return Array.from(filesToFormat);
77+
return {
78+
filesToFormat: Array.from(filesToFormat),
79+
pnpmBuildDependencies: allPnpmBuildDependencies
80+
};
6881
}
6982

7083
export function setupAddons(
@@ -91,7 +104,7 @@ type RunAddon = {
91104
addon: Addon<Record<string, Question>>;
92105
multiple: boolean;
93106
};
94-
async function runAddon({ addon, multiple, workspace }: RunAddon): Promise<string[]> {
107+
async function runAddon({ addon, multiple, workspace }: RunAddon) {
95108
const files = new Set<string>();
96109

97110
// apply default addon options
@@ -103,6 +116,7 @@ async function runAddon({ addon, multiple, workspace }: RunAddon): Promise<strin
103116
}
104117

105118
const dependencies: Array<{ pkg: string; version: string; dev: boolean }> = [];
119+
const pnpmBuildDependencies: string[] = [];
106120
const sv: SvApi = {
107121
file: (path, content) => {
108122
try {
@@ -150,14 +164,20 @@ async function runAddon({ addon, multiple, workspace }: RunAddon): Promise<strin
150164
},
151165
devDependency: (pkg, version) => {
152166
dependencies.push({ pkg, version, dev: true });
167+
},
168+
pnpmBuildDependendency: (pkg) => {
169+
pnpmBuildDependencies.push(pkg);
153170
}
154171
};
155172
await addon.run({ ...workspace, sv });
156173

157174
const pkgPath = installPackages(dependencies, workspace);
158175
files.add(pkgPath);
159176

160-
return Array.from(files);
177+
return {
178+
files: Array.from(files),
179+
pnpmBuildDependencies
180+
};
161181
}
162182

163183
// sorts them to their execution order

packages/cli/lib/testing.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { exec } from 'tinyexec';
66
import { create } from '@sveltejs/create';
77
import pstree, { type PS } from 'ps-tree';
88

9+
export { addPnpmBuildDependendencies } from '../utils/package-manager.ts';
910
export type ProjectVariant = 'kit-js' | 'kit-ts' | 'vite-js' | 'vite-ts';
1011

1112
const TEMPLATES_DIR = '.templates';

packages/cli/utils/package-manager.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
13
import process from 'node:process';
4+
import * as find from 'empathic/find';
25
import { exec } from 'tinyexec';
36
import * as p from '@sveltejs/clack-prompts';
47
import {
@@ -8,6 +11,7 @@ import {
811
detectSync,
912
type AgentName
1013
} from 'package-manager-detector';
14+
import { parseJson } from '@sveltejs/cli-core/parsers';
1115

1216
const agents = AGENTS.filter((agent): agent is AgentName => !agent.includes('@'));
1317
const agentOptions: PackageManagerOptions = agents.map((pm) => ({ value: pm, label: pm }));
@@ -67,3 +71,33 @@ export function getUserAgent(): AgentName | undefined {
6771
const name = pmSpec.substring(0, separatorPos) as AgentName;
6872
return AGENTS.includes(name) ? name : undefined;
6973
}
74+
75+
export function addPnpmBuildDependendencies(
76+
cwd: string,
77+
packageManager: AgentName | null | undefined,
78+
allowedPackages: string[]
79+
) {
80+
// other package managers are currently not affected by this change
81+
if (!packageManager || packageManager !== 'pnpm') return;
82+
83+
// find the workspace root
84+
const pnpmWorkspacePath = find.up('pnpm-workspace.yaml', { cwd });
85+
if (!pnpmWorkspacePath) return;
86+
87+
// load the package.json
88+
const pkgPath = path.join(path.dirname(pnpmWorkspacePath), 'package.json');
89+
const content = fs.readFileSync(pkgPath, 'utf-8');
90+
const { data, generateCode } = parseJson(content);
91+
92+
// add the packages where we install scripts should be executed
93+
data.pnpm ??= {};
94+
data.pnpm.onlyBuiltDependencies ??= [];
95+
for (const allowedPackage of allowedPackages) {
96+
if (data.pnpm.onlyBuiltDependencies.includes(allowedPackage)) continue;
97+
data.pnpm.onlyBuiltDependencies.push(allowedPackage);
98+
}
99+
100+
// save the updated package.json
101+
const newContent = generateCode();
102+
fs.writeFileSync(pkgPath, newContent);
103+
}

0 commit comments

Comments
 (0)