Skip to content

Commit 04e1541

Browse files
Revert "Revert "Fix corepack packageManager detection on monorepos"" (#11871)
Reverts #11865 --------- Co-authored-by: Austin Merrick <[email protected]> Co-authored-by: Austin Merrick <[email protected]>
1 parent 6b75d50 commit 04e1541

File tree

16 files changed

+185
-59
lines changed

16 files changed

+185
-59
lines changed

.changeset/wild-needles-glow.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@vercel/build-utils": patch
3+
"@vercel/hydrogen": patch
4+
"@vercel/next": patch
5+
"@vercel/redwood": patch
6+
"@vercel/remix-builder": patch
7+
"@vercel/static-build": patch
8+
---
9+
10+
Revert "Revert "Fix corepack `packageManager` detection on monorepos""

packages/build-utils/src/fs/run-user-scripts.ts

Lines changed: 58 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ export interface ScanParentDirsResult {
5959
* or `undefined` if not found.
6060
*/
6161
lockfileVersion?: number;
62+
/**
63+
* The contents of the `packageManager` field from `package.json` if found.
64+
* The value may come from a different `package.json` file than the one
65+
* specified by `packageJsonPath`, in the case of a monorepo.
66+
*/
67+
packageJsonPackageManager?: string;
6268
}
6369

6470
export interface TraverseUpDirectoriesProps {
@@ -316,17 +322,19 @@ export async function scanParentDirs(
316322
readPackageJson && pkgJsonPath
317323
? JSON.parse(await fs.readFile(pkgJsonPath, 'utf8'))
318324
: undefined;
319-
const [yarnLockPath, npmLockPath, pnpmLockPath, bunLockPath] =
320-
await walkParentDirsMulti({
321-
base,
322-
start: destPath,
323-
filenames: [
324-
'yarn.lock',
325-
'package-lock.json',
326-
'pnpm-lock.yaml',
327-
'bun.lockb',
328-
],
329-
});
325+
const {
326+
paths: [yarnLockPath, npmLockPath, pnpmLockPath, bunLockPath],
327+
packageJsonPackageManager,
328+
} = await walkParentDirsMulti({
329+
base,
330+
start: destPath,
331+
filenames: [
332+
'yarn.lock',
333+
'package-lock.json',
334+
'pnpm-lock.yaml',
335+
'bun.lockb',
336+
],
337+
});
330338
let lockfilePath: string | undefined;
331339
let lockfileVersion: number | undefined;
332340
let cliType: CliType;
@@ -366,23 +374,25 @@ export async function scanParentDirs(
366374
// TODO: read "bun-lockfile-format-v0"
367375
lockfileVersion = 0;
368376
} else {
369-
cliType = packageJson
370-
? detectPackageManagerNameWithoutLockfile(packageJson)
371-
: 'npm';
377+
cliType = detectPackageManagerNameWithoutLockfile(
378+
packageJsonPackageManager
379+
);
372380
}
373381

374382
const packageJsonPath = pkgJsonPath || undefined;
375383
return {
376384
cliType,
377385
packageJson,
386+
packageJsonPackageManager,
378387
lockfilePath,
379388
lockfileVersion,
380389
packageJsonPath,
381390
};
382391
}
383392

384-
function detectPackageManagerNameWithoutLockfile(packageJson: PackageJson) {
385-
const packageJsonPackageManager = packageJson.packageManager;
393+
function detectPackageManagerNameWithoutLockfile(
394+
packageJsonPackageManager?: string
395+
) {
386396
if (usingCorepack(process.env, packageJsonPackageManager)) {
387397
const corepackPackageManager = validateVersionSpecifier(
388398
packageJsonPackageManager
@@ -436,20 +446,35 @@ async function walkParentDirsMulti({
436446
base,
437447
start,
438448
filenames,
439-
}: WalkParentDirsMultiProps): Promise<(string | undefined)[]> {
449+
}: WalkParentDirsMultiProps): Promise<{
450+
paths: (string | undefined)[];
451+
packageJsonPackageManager?: string;
452+
}> {
453+
let packageManager: string | undefined;
454+
440455
for (const dir of traverseUpDirectories({ start, base })) {
441456
const fullPaths = filenames.map(f => path.join(dir, f));
442457
const existResults = await Promise.all(
443458
fullPaths.map(f => fs.pathExists(f))
444459
);
445460
const foundOneOrMore = existResults.some(b => b);
461+
const packageJsonPath = path.join(dir, 'package.json');
462+
const packageJson: PackageJson | null = await fs
463+
.readJSON(packageJsonPath)
464+
.catch(() => null);
465+
if (packageJson?.packageManager) {
466+
packageManager = packageJson.packageManager;
467+
}
446468

447469
if (foundOneOrMore) {
448-
return fullPaths.map((f, i) => (existResults[i] ? f : undefined));
470+
return {
471+
paths: fullPaths.map((f, i) => (existResults[i] ? f : undefined)),
472+
packageJsonPackageManager: packageManager,
473+
};
449474
}
450475
}
451476

452-
return [];
477+
return { paths: [], packageJsonPackageManager: packageManager };
453478
}
454479

455480
function isSet<T>(v: any): v is Set<T> {
@@ -472,8 +497,13 @@ export async function runNpmInstall(
472497

473498
try {
474499
await runNpmInstallSema.acquire();
475-
const { cliType, packageJsonPath, packageJson, lockfileVersion } =
476-
await scanParentDirs(destPath, true);
500+
const {
501+
cliType,
502+
packageJsonPath,
503+
lockfileVersion,
504+
packageJsonPackageManager,
505+
packageJson,
506+
} = await scanParentDirs(destPath);
477507

478508
if (!packageJsonPath) {
479509
debug(
@@ -508,7 +538,7 @@ export async function runNpmInstall(
508538
opts.env = getEnvForPackageManager({
509539
cliType,
510540
lockfileVersion,
511-
packageJsonPackageManager: packageJson?.packageManager,
541+
packageJsonPackageManager,
512542
nodeVersion,
513543
env,
514544
packageJsonEngines: packageJson?.engines,
@@ -1041,14 +1071,12 @@ export async function runCustomInstallCommand({
10411071
spawnOpts?: SpawnOptions;
10421072
}) {
10431073
console.log(`Running "install" command: \`${installCommand}\`...`);
1044-
const { cliType, lockfileVersion, packageJson } = await scanParentDirs(
1045-
destPath,
1046-
true
1047-
);
1074+
const { cliType, lockfileVersion, packageJsonPackageManager, packageJson } =
1075+
await scanParentDirs(destPath);
10481076
const env = getEnvForPackageManager({
10491077
cliType,
10501078
lockfileVersion,
1051-
packageJsonPackageManager: packageJson?.packageManager,
1079+
packageJsonPackageManager,
10521080
nodeVersion,
10531081
env: spawnOpts?.env || {},
10541082
packageJsonEngines: packageJson?.engines,
@@ -1068,10 +1096,8 @@ export async function runPackageJsonScript(
10681096
) {
10691097
assert(path.isAbsolute(destPath));
10701098

1071-
const { packageJson, cliType, lockfileVersion } = await scanParentDirs(
1072-
destPath,
1073-
true
1074-
);
1099+
const { packageJson, cliType, lockfileVersion, packageJsonPackageManager } =
1100+
await scanParentDirs(destPath, true);
10751101
const scriptName = getScriptName(
10761102
packageJson,
10771103
typeof scriptNames === 'string' ? [scriptNames] : scriptNames
@@ -1087,7 +1113,7 @@ export async function runPackageJsonScript(
10871113
env: getEnvForPackageManager({
10881114
cliType,
10891115
lockfileVersion,
1090-
packageJsonPackageManager: packageJson?.packageManager,
1116+
packageJsonPackageManager,
10911117
nodeVersion: undefined,
10921118
env: cloneEnv(process.env, spawnOpts?.env),
10931119
packageJsonEngines: packageJson?.engines,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "a21",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"debug": "^4.3.2"
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "b21",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"cowsay": "^1.5.0"
14+
}
15+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"private": true,
3+
"name": "41-npm-workspaces-corepack",
4+
"version": "1.0.0",
5+
"packageManager": "[email protected]",
6+
"workspaces": [
7+
"a",
8+
"b"
9+
]
10+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "build-c23",
3+
"license": "MIT",
4+
"version": "0.1.0"
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "build-d23",
3+
"license": "MIT",
4+
"version": "0.1.0",
5+
"devDependencies": {
6+
"once": "1.4.0"
7+
}
8+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"private": true,
3+
"name": "42-pnpm-workspaces-corepack",
4+
"packageManager": "[email protected]",
5+
"license": "MIT",
6+
"version": "1.0.0"
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
packages:
2+
- 'c'
3+
- 'd'

packages/build-utils/test/unit.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,43 @@ it('should detect package.json in nested frontend', async () => {
713713
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
714714
});
715715

716+
it('should detect `packageManager` in npm monorepo', async () => {
717+
try {
718+
process.env.ENABLE_EXPERIMENTAL_COREPACK = '1';
719+
720+
const base = path.join(__dirname, 'fixtures', '41-npm-workspaces-corepack');
721+
const fixture = path.join(base, 'a');
722+
const result = await scanParentDirs(fixture, false, base);
723+
console.log(result);
724+
expect(result.cliType).toEqual('npm');
725+
expect(result.packageJsonPackageManager).toEqual('[email protected]');
726+
expect(result.lockfileVersion).toEqual(undefined);
727+
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
728+
} finally {
729+
delete process.env.ENABLE_EXPERIMENTAL_COREPACK;
730+
}
731+
});
732+
733+
it('should detect `packageManager` in pnpm monorepo', async () => {
734+
try {
735+
process.env.ENABLE_EXPERIMENTAL_COREPACK = '1';
736+
const base = path.join(
737+
__dirname,
738+
'fixtures',
739+
'42-pnpm-workspaces-corepack'
740+
);
741+
const fixture = path.join(base, 'c');
742+
const result = await scanParentDirs(fixture, false, base);
743+
console.log(result);
744+
expect(result.cliType).toEqual('pnpm');
745+
expect(result.packageJsonPackageManager).toEqual('[email protected]');
746+
expect(result.lockfileVersion).toEqual(undefined);
747+
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
748+
} finally {
749+
delete process.env.ENABLE_EXPERIMENTAL_COREPACK;
750+
}
751+
});
752+
716753
it('should retry npm install when peer deps invalid and npm@8 on node@16', async () => {
717754
const nodeMajor = Number(process.versions.node.split('.')[0]);
718755
if (nodeMajor !== 16) {

0 commit comments

Comments
 (0)