fix(core)!: respect packageManager field in package.json when detecting version (#29249)
Attept to read package manager version from config before invoking package manager CLI BREAK CHANGE: If you have a mismatch between the `packageManager` field in `package.json` and the actual version installed in the environment, it may lead to unexpected behavior when installing. This should not be a problem if you are using corepack already. ## Related Issue(s) https://github.com/nrwl/nx/issues/29244 --------- Co-authored-by: Jack Hsu <jack.hsu@gmail.com>
This commit is contained in:
parent
d89b7743c6
commit
6610f3d632
@ -15,6 +15,7 @@ import {
|
|||||||
isWorkspacesEnabled,
|
isWorkspacesEnabled,
|
||||||
modifyYarnRcToFitNewDirectory,
|
modifyYarnRcToFitNewDirectory,
|
||||||
modifyYarnRcYmlToFitNewDirectory,
|
modifyYarnRcYmlToFitNewDirectory,
|
||||||
|
parseVersionFromPackageManagerField,
|
||||||
PackageManager,
|
PackageManager,
|
||||||
} from './package-manager';
|
} from './package-manager';
|
||||||
|
|
||||||
@ -521,4 +522,29 @@ describe('package-manager', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('parseVersionFromPackageManagerField', () => {
|
||||||
|
it('should return null for invalid semver', () => {
|
||||||
|
expect(parseVersionFromPackageManagerField('yarn', 'bad')).toEqual(null);
|
||||||
|
expect(parseVersionFromPackageManagerField('yarn', '2.1')).toEqual(null);
|
||||||
|
expect(
|
||||||
|
parseVersionFromPackageManagerField(
|
||||||
|
'yarn',
|
||||||
|
'https://registry.npmjs.org/@yarnpkg/cli-dist/-/cli-dist-3.2.3.tgz#sha224.16a0797d1710d1fb7ec40ab5c3801b68370a612a9b66ba117ad9924b'
|
||||||
|
)
|
||||||
|
).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should <major>.<minor>.<patch> version', () => {
|
||||||
|
expect(parseVersionFromPackageManagerField('yarn', 'yarn@3.2.3')).toEqual(
|
||||||
|
'3.2.3'
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
parseVersionFromPackageManagerField(
|
||||||
|
'yarn',
|
||||||
|
'yarn@3.2.3+sha224.953c8233f7a92884eee2de69a1b92d1f2ec1655e66d08071ba9a02fa'
|
||||||
|
)
|
||||||
|
).toEqual('3.2.3');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
} from 'yaml';
|
} from 'yaml';
|
||||||
import { rm } from 'node:fs/promises';
|
import { rm } from 'node:fs/promises';
|
||||||
import { dirname, join, relative } from 'path';
|
import { dirname, join, relative } from 'path';
|
||||||
import { gte, lt } from 'semver';
|
import { gte, lt, parse } from 'semver';
|
||||||
import { dirSync } from 'tmp';
|
import { dirSync } from 'tmp';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
|
|
||||||
@ -213,29 +213,24 @@ export function getPackageManagerVersion(
|
|||||||
packageManager: PackageManager = detectPackageManager(),
|
packageManager: PackageManager = detectPackageManager(),
|
||||||
cwd = process.cwd()
|
cwd = process.cwd()
|
||||||
): string {
|
): string {
|
||||||
let version;
|
let version: string;
|
||||||
try {
|
if (existsSync(join(cwd, 'package.json'))) {
|
||||||
version = execSync(`${packageManager} --version`, {
|
const packageManagerEntry = readJsonFile<PackageJson>(
|
||||||
cwd,
|
join(cwd, 'package.json')
|
||||||
encoding: 'utf-8',
|
)?.packageManager;
|
||||||
windowsHide: true,
|
version = parseVersionFromPackageManagerField(
|
||||||
}).trim();
|
packageManager,
|
||||||
} catch {
|
packageManagerEntry
|
||||||
if (existsSync(join(cwd, 'package.json'))) {
|
);
|
||||||
const packageVersion = readJsonFile<PackageJson>(
|
}
|
||||||
join(cwd, 'package.json')
|
if (!version) {
|
||||||
)?.packageManager;
|
try {
|
||||||
if (packageVersion) {
|
version = execSync(`${packageManager} --version`, {
|
||||||
const [packageManagerFromPackageJson, versionFromPackageJson] =
|
cwd,
|
||||||
packageVersion.split('@');
|
encoding: 'utf-8',
|
||||||
if (
|
windowsHide: true,
|
||||||
packageManagerFromPackageJson === packageManager &&
|
}).trim();
|
||||||
versionFromPackageJson
|
} catch {}
|
||||||
) {
|
|
||||||
version = versionFromPackageJson;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!version) {
|
if (!version) {
|
||||||
throw new Error(`Cannot determine the version of ${packageManager}.`);
|
throw new Error(`Cannot determine the version of ${packageManager}.`);
|
||||||
@ -243,6 +238,29 @@ export function getPackageManagerVersion(
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseVersionFromPackageManagerField(
|
||||||
|
requestedPackageManager: string,
|
||||||
|
packageManagerFieldValue: string | undefined
|
||||||
|
): null | string {
|
||||||
|
if (!packageManagerFieldValue) return null;
|
||||||
|
const [packageManagerFromPackageJson, versionFromPackageJson] =
|
||||||
|
packageManagerFieldValue.split('@');
|
||||||
|
if (
|
||||||
|
versionFromPackageJson &&
|
||||||
|
// If it's a URL, it's not a valid range by default, unless users set `COREPACK_ENABLE_UNSAFE_CUSTOM_URLS=1`.
|
||||||
|
// In the unsafe case, there's no way to reliably pare out the version since it could be anything, e.g. http://mydomain.com/bin/yarn.js.
|
||||||
|
// See: https://github.com/nodejs/corepack/blob/2b43f26/sources/corepackUtils.ts#L110-L112
|
||||||
|
!URL.canParse(versionFromPackageJson) &&
|
||||||
|
packageManagerFromPackageJson === requestedPackageManager &&
|
||||||
|
versionFromPackageJson
|
||||||
|
) {
|
||||||
|
// The range could have a validation hash attached, like "3.2.3+sha224.953c8233f7a92884eee2de69a1b92d1f2ec1655e66d08071ba9a02fa".
|
||||||
|
// We just want to parse out the "<major>.<minor>.<patch>". Semver treats "+" as a build, which is not included in the resulting version.
|
||||||
|
return parse(versionFromPackageJson)?.version ?? null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for a project level npmrc file by crawling up the file tree until
|
* Checks for a project level npmrc file by crawling up the file tree until
|
||||||
* hitting a package.json file, as this is how npm finds them as well.
|
* hitting a package.json file, as this is how npm finds them as well.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user