Skip to content

Commit 40eff80

Browse files
JPeer264claude
andauthored
chore: Use the isolated pnpm node_modules layout (#25368)
This moves the monorepo from the hoisted `node_modules` layout to the isolated pnpm default. Each package now declares the packages that it imports, instead of using what the root folder had. A full build gives the same files as with the hoisted layout. - Packages declare the dependencies that they imported from the hoisted root folder, with the version that the root folder had. The lockfile gets no new versions. - The root `package.json` no longer lists every workspace package as `workspace:*`. Each package declares the workspace packages that it imports, for example `@sentry-internal/rollup-utils` in the rollup configs. The root keeps three of them: `@sentry-internal/node-integration-tests` (its suites import it by name, also from tmp folders with their own `package.json`), `@sentry/eslint-plugin-sdk` (oxlint plugin) and `@sentry/sveltekit` (size-limit bundles its self-import). - `@sentry/replay` declares `@sentry/conventions` as a dev dependency, so its build still inlines the constant from there. - rollup-utils reads the `sideEffects` field of external packages from the nearest `node_modules` of the built package, not only from the root folder, and warns when it does not find an installed package there. - The integration test runner adds the bins of node-integration-tests to the `PATH` of setup commands (for example `prisma generate`), because the Bun and Deno packages run the shared suites with their own `PATH`. ## packageExtensions decisions `@apm-js-collab/code-transformer-bundler-plugins` imports the types of Vite, Rollup, webpack and esbuild without declaring them. `pnpm-workspace.yaml` declares them as optional peers, so its types use the Vite of the importing package. The real fix belongs upstream: apm-js-collab/code-transformer-bundler-plugins#49. ## Peer decisions `autoInstallPeers` stays `false`. The pnpm default (`true`) adds 674 package versions for peers that no package needs, for example `@glimmer/component` and the GraphQL tools of Gatsby. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 09fb95c commit 40eff80

62 files changed

Lines changed: 536 additions & 295 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎dev-packages/browser-integration-tests/package.json‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@
7272
"webpack": "^5.95.0"
7373
},
7474
"devDependencies": {
75+
"@sentry/browser-utils": "11.8.0",
76+
"@sentry/core": "11.8.0",
77+
"@sentry/replay-canvas": "11.8.0",
78+
"@sentry/rrweb-types": "^2.44.1",
79+
"@sentry/wasm": "11.8.0",
7580
"@types/node": "^20.19.0",
7681
"eslint-plugin-regexp": "^3.1.0",
7782
"glob": "^13.0.6"

‎dev-packages/bun-integration-tests/package.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
"devDependencies": {
2424
"@sentry-internal/node-integration-tests": "11.8.0",
2525
"@sentry-internal/test-utils": "11.8.0",
26+
"@sentry/core": "11.8.0",
27+
"@types/mysql": "^2.15.27",
28+
"@types/pg": "^8.20.0",
2629
"bun-types": "^1.2.9",
2730
"vitest": "^4.1.11"
2831
},

‎dev-packages/bundler-plugin-integration-tests/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
},
1717
"devDependencies": {
1818
"premove": "^4.0.0",
19+
"sentry": "^0.45.0",
1920
"typescript": "~6.0.3",
2021
"vitest": "^4.1.11"
2122
},

‎dev-packages/cloudflare-integration-tests/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@cloudflare/vite-plugin": "1.34.0",
3535
"@cloudflare/workers-types": "^4.20260426.0",
3636
"@cloudflare/workers-types-v5": "npm:@cloudflare/workers-types@5.20260710.1",
37+
"@opentelemetry/api": "^1.9.1",
3738
"@sentry-internal/test-utils": "11.8.0",
3839
"@sentry/conventions": "0.27.0",
3940
"eslint-plugin-regexp": "^3.1.0",

‎dev-packages/deno-integration-tests/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
},
2424
"devDependencies": {
2525
"@sentry-internal/node-integration-tests": "11.8.0",
26+
"@sentry/conventions": "^0.27.0",
2627
"vitest": "^4.1.11"
2728
},
2829
"volta": {

‎dev-packages/node-integration-tests/package.json‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@
5656
"@sentry/core": "11.8.0",
5757
"@sentry/hono": "11.8.0",
5858
"@sentry/node": "11.8.0",
59+
"@sentry/server-runtime-injection": "11.8.0",
5960
"@sentry/server-utils": "11.8.0",
61+
"@smithy/node-http-handler": "4.12.1",
6062
"@types/mongodb": "^3.6.20",
6163
"@types/mysql": "^2.15.21",
6264
"@types/pg": "^8.6.5",
@@ -112,17 +114,22 @@
112114
"yargs": "^16.2.0"
113115
},
114116
"devDependencies": {
117+
"@hapi/boom": "^10.0.1",
115118
"@neondatabase/serverless": "1.2.0",
116119
"@opentelemetry/instrumentation-http": "0.220.0",
120+
"@sentry-internal/rollup-utils": "11.8.0",
117121
"@sentry/conventions": "0.27.0",
118122
"@sentry-internal/test-utils": "11.8.0",
123+
"@sentry/node-native": "11.8.0",
119124
"@types/amqplib": "^0.10.5",
125+
"@types/express": "^4.17.21",
120126
"@types/node-cron": "^3.0.11",
121127
"@types/node-schedule": "^2.1.7",
122128
"esbuild": "0.28.1",
123129
"eslint-plugin-regexp": "^3.1.0",
124130
"globby": "11",
125131
"react": "^18.3.1",
132+
"winston-transport": "^4.9.0",
126133
"zod": "^3.24.1"
127134
},
128135
"config": {

‎dev-packages/node-integration-tests/utils/runner/createEsmAndCjsTests.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { exec } from 'child_process';
22
import { existsSync } from 'fs';
33
import { cp, mkdir, readFile, rm, writeFile } from 'fs/promises';
4-
import { basename, join } from 'path';
4+
import { basename, delimiter, join } from 'path';
55
import { promisify } from 'util';
66
import { afterAll, beforeAll, test, type TestAPI } from 'vitest';
7-
import { CLEANUP_STEPS, createRunner } from './createRunner';
7+
import { CLEANUP_STEPS, createRunner, PACKAGE_ROOT } from './createRunner';
88

99
const execPromise = promisify(exec);
1010

@@ -267,7 +267,10 @@ function prepareTmpDir(
267267
}
268268

269269
if (options?.afterSetupCommand) {
270-
const env = { ...process.env, PATH: `${join(tmpDirPath, 'node_modules', '.bin')}:${process.env.PATH}` };
270+
// The Bun and Deno packages run these suites with their own `PATH`, which does not have the bins of
271+
// this package (e.g. `prisma`).
272+
const binPaths = [join(tmpDirPath, 'node_modules', '.bin'), join(PACKAGE_ROOT, 'node_modules', '.bin')];
273+
const env = { ...process.env, PATH: [...binPaths, process.env.PATH].join(delimiter) };
271274
await execPromise(options.afterSetupCommand, { cwd: tmpDirPath, env });
272275
}
273276
}

‎dev-packages/node-integration-tests/utils/runner/createRunner.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ const COMPILE_CACHE_ENV: Record<string, string> =
121121

122122
// The Bun and Deno packages run these suites from their own folder. Scenarios read the working
123123
// directory (e.g. `modulesIntegration` reads its `package.json`), so it is always this package.
124-
const PACKAGE_ROOT = join(__dirname, '..', '..');
124+
export const PACKAGE_ROOT = join(__dirname, '..', '..');
125125

126126
/** Node flags that preload a module before the entry point. */
127127
const PRELOAD_FLAGS = ['--import', '--require', '-r'];

‎dev-packages/rollup-utils/npmHelpers.mjs‎

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import * as fs from 'fs';
8-
import { builtinModules } from 'module';
8+
import { builtinModules, isBuiltin } from 'module';
99
import * as path from 'path';
1010
import { fileURLToPath } from 'url';
1111

@@ -44,18 +44,49 @@ function isSideEffectFreePackage(packageName) {
4444

4545
let sideEffectFree = false;
4646
try {
47-
const json = JSON.parse(
48-
fs.readFileSync(path.resolve(repoRoot, 'node_modules', packageName, 'package.json'), { encoding: 'utf8' }),
49-
);
47+
const json = JSON.parse(fs.readFileSync(findPackageJson(packageName), { encoding: 'utf8' }));
5048
sideEffectFree = json.sideEffects === false;
5149
} catch {
52-
// Not resolvable from the repo root - assume side effects.
50+
// Not resolvable from the built package - assume side effects.
51+
if (isInstalledPackage(packageName)) {
52+
// oxlint-disable-next-line no-console
53+
console.warn(
54+
`[rollup-utils] Cannot read the package.json of "${packageName}" from ${process.cwd()}, so its imports are kept. Declare it in the package.json of the built package.`,
55+
);
56+
}
5357
}
5458

5559
sideEffectsCache.set(packageName, sideEffectFree);
5660
return sideEffectFree;
5761
}
5862

63+
/**
64+
* The `package.json` of a dependency of the built package (the working directory): the nearest
65+
* `node_modules/<packageName>` from there up to the repo root, like Node resolves it.
66+
*/
67+
function findPackageJson(packageName) {
68+
for (let dir = process.cwd(); ; dir = path.dirname(dir)) {
69+
const candidate = path.join(dir, 'node_modules', packageName, 'package.json');
70+
if (fs.existsSync(candidate) || dir === repoRoot || dir === path.dirname(dir)) {
71+
return candidate;
72+
}
73+
}
74+
}
75+
76+
/**
77+
* Whether `packageName` must have a `package.json` in a `node_modules` folder of the built package. This is false for
78+
* builtins, for virtual modules and placeholders (e.g. `$app`, `cloudflare:workers`), which are no valid npm package
79+
* names, for the built package itself, and for optional peers, which are not always installed.
80+
*/
81+
function isInstalledPackage(packageName) {
82+
return (
83+
!isBuiltin(packageName) &&
84+
/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(packageName) &&
85+
packageName !== packageDotJSON.name &&
86+
!packageDotJSON.peerDependenciesMeta?.[packageName]?.optional
87+
);
88+
}
89+
5990
function getPackageName(id) {
6091
const segments = id.split('/');
6192
return id.startsWith('@') ? segments.slice(0, 2).join('/') : segments[0];

‎dev-packages/test-utils/package.json‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@
6666
},
6767
"devDependencies": {
6868
"@playwright/test": "1.64.1",
69+
"@sentry-internal/rollup-utils": "11.8.0",
6970
"@sentry/core": "11.8.0",
71+
"@types/express": "^4.17.21",
7072
"@types/ws": "^8.18.1",
7173
"eslint-plugin-regexp": "^3.1.0"
7274
},

0 commit comments

Comments
 (0)