mirror of https://github.com/nodejs/corepack.git
fix: improve support for `COREPACK_NPM_REGISTRY` with Yarn Berry (#396)
This commit is contained in:
parent
88d504c1a5
commit
47be27c9db
|
|
@ -199,7 +199,6 @@ export class Engine {
|
||||||
const packageManagerInfo = await corepackUtils.installVersion(folderUtils.getInstallFolder(), locator, {
|
const packageManagerInfo = await corepackUtils.installVersion(folderUtils.getInstallFolder(), locator, {
|
||||||
spec,
|
spec,
|
||||||
});
|
});
|
||||||
spec.bin ??= packageManagerInfo.bin;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...packageManagerInfo,
|
...packageManagerInfo,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import * as httpUtils from './httpUtils
|
||||||
import * as nodeUtils from './nodeUtils';
|
import * as nodeUtils from './nodeUtils';
|
||||||
import * as npmRegistryUtils from './npmRegistryUtils';
|
import * as npmRegistryUtils from './npmRegistryUtils';
|
||||||
import {RegistrySpec, Descriptor, Locator, PackageManagerSpec} from './types';
|
import {RegistrySpec, Descriptor, Locator, PackageManagerSpec} from './types';
|
||||||
|
import {BinList, BinSpec, InstallSpec} from './types';
|
||||||
|
|
||||||
export function getRegistryFromPackageManagerSpec(spec: PackageManagerSpec) {
|
export function getRegistryFromPackageManagerSpec(spec: PackageManagerSpec) {
|
||||||
return process.env.COREPACK_NPM_REGISTRY
|
return process.env.COREPACK_NPM_REGISTRY
|
||||||
|
|
@ -124,7 +125,15 @@ function parseURLReference(locator: Locator) {
|
||||||
return {version: encodeURIComponent(href), build: []};
|
return {version: encodeURIComponent(href), build: []};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function installVersion(installTarget: string, locator: Locator, {spec}: {spec: PackageManagerSpec}) {
|
function isValidBinList(x: unknown): x is BinList {
|
||||||
|
return Array.isArray(x) && x.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidBinSpec(x: unknown): x is BinSpec {
|
||||||
|
return typeof x === `object` && x !== null && !Array.isArray(x) && Object.keys(x).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function installVersion(installTarget: string, locator: Locator, {spec}: {spec: PackageManagerSpec}): Promise<InstallSpec> {
|
||||||
const locatorIsASupportedPackageManager = isSupportedPackageManagerLocator(locator);
|
const locatorIsASupportedPackageManager = isSupportedPackageManagerLocator(locator);
|
||||||
const locatorReference = locatorIsASupportedPackageManager ? semver.parse(locator.reference)! : parseURLReference(locator);
|
const locatorReference = locatorIsASupportedPackageManager ? semver.parse(locator.reference)! : parseURLReference(locator);
|
||||||
const {version, build} = locatorReference;
|
const {version, build} = locatorReference;
|
||||||
|
|
@ -152,13 +161,18 @@ export async function installVersion(installTarget: string, locator: Locator, {s
|
||||||
|
|
||||||
let url: string;
|
let url: string;
|
||||||
if (locatorIsASupportedPackageManager) {
|
if (locatorIsASupportedPackageManager) {
|
||||||
const defaultNpmRegistryURL = spec.url.replace(`{}`, version);
|
url = spec.url.replace(`{}`, version);
|
||||||
url = process.env.COREPACK_NPM_REGISTRY ?
|
if (process.env.COREPACK_NPM_REGISTRY) {
|
||||||
defaultNpmRegistryURL.replace(
|
const registry = getRegistryFromPackageManagerSpec(spec);
|
||||||
npmRegistryUtils.DEFAULT_NPM_REGISTRY_URL,
|
if (registry.type === `npm`) {
|
||||||
() => process.env.COREPACK_NPM_REGISTRY!,
|
url = await npmRegistryUtils.fetchTarballUrl(registry.package, version);
|
||||||
) :
|
} else {
|
||||||
defaultNpmRegistryURL;
|
url = url.replace(
|
||||||
|
npmRegistryUtils.DEFAULT_NPM_REGISTRY_URL,
|
||||||
|
() => process.env.COREPACK_NPM_REGISTRY!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
url = decodeURIComponent(version);
|
url = decodeURIComponent(version);
|
||||||
}
|
}
|
||||||
|
|
@ -191,13 +205,34 @@ export async function installVersion(installTarget: string, locator: Locator, {s
|
||||||
const hash = stream.pipe(createHash(algo));
|
const hash = stream.pipe(createHash(algo));
|
||||||
await once(sendTo, `finish`);
|
await once(sendTo, `finish`);
|
||||||
|
|
||||||
let bin;
|
let bin: BinSpec | BinList;
|
||||||
if (!locatorIsASupportedPackageManager) {
|
const isSingleFile = outputFile !== null;
|
||||||
if (ext === `.tgz`) {
|
|
||||||
bin = require(path.join(tmpFolder, `package.json`)).bin;
|
// In config, yarn berry is expected to be downloaded as a single file,
|
||||||
} else if (ext === `.js`) {
|
// and therefore `spec.bin` is an array. However, when dowloaded from
|
||||||
|
// custom npm registry as tarball, `bin` should be a map.
|
||||||
|
// In this case, we ignore the configured `spec.bin`.
|
||||||
|
|
||||||
|
if (isSingleFile) {
|
||||||
|
if (locatorIsASupportedPackageManager && isValidBinList(spec.bin)) {
|
||||||
|
bin = spec.bin;
|
||||||
|
} else {
|
||||||
bin = [locator.name];
|
bin = [locator.name];
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (locatorIsASupportedPackageManager && isValidBinSpec(spec.bin)) {
|
||||||
|
bin = spec.bin;
|
||||||
|
} else {
|
||||||
|
const {name: packageName, bin: packageBin} = require(path.join(tmpFolder, `package.json`));
|
||||||
|
if (typeof packageBin === `string`) {
|
||||||
|
// When `bin` is a string, the name of the executable is the name of the package.
|
||||||
|
bin = {[packageName]: packageBin};
|
||||||
|
} else if (isValidBinSpec(packageBin)) {
|
||||||
|
bin = packageBin;
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unable to locate bin in package.json`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const actualHash = hash.digest(`hex`);
|
const actualHash = hash.digest(`hex`);
|
||||||
|
|
@ -292,10 +327,11 @@ async function renameUnderWindows(oldPath: fs.PathLike, newPath: fs.PathLike) {
|
||||||
/**
|
/**
|
||||||
* Loads the binary, taking control of the current process.
|
* Loads the binary, taking control of the current process.
|
||||||
*/
|
*/
|
||||||
export async function runVersion(locator: Locator, installSpec: { location: string, spec: PackageManagerSpec }, binName: string, args: Array<string>): Promise<void> {
|
export async function runVersion(locator: Locator, installSpec: InstallSpec & {spec: PackageManagerSpec}, binName: string, args: Array<string>): Promise<void> {
|
||||||
let binPath: string | null = null;
|
let binPath: string | null = null;
|
||||||
if (Array.isArray(installSpec.spec.bin)) {
|
const bin = installSpec.bin ?? installSpec.spec.bin;
|
||||||
if (installSpec.spec.bin.some(bin => bin === binName)) {
|
if (Array.isArray(bin)) {
|
||||||
|
if (bin.some(name => name === binName)) {
|
||||||
const parsedUrl = new URL(installSpec.spec.url);
|
const parsedUrl = new URL(installSpec.spec.url);
|
||||||
const ext = path.posix.extname(parsedUrl.pathname);
|
const ext = path.posix.extname(parsedUrl.pathname);
|
||||||
if (ext === `.js`) {
|
if (ext === `.js`) {
|
||||||
|
|
@ -303,7 +339,7 @@ export async function runVersion(locator: Locator, installSpec: { location: stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const [name, dest] of Object.entries(installSpec.spec.bin)) {
|
for (const [name, dest] of Object.entries(bin)) {
|
||||||
if (name === binName) {
|
if (name === binName) {
|
||||||
binPath = path.join(installSpec.location, dest);
|
binPath = path.join(installSpec.location, dest);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -48,3 +48,16 @@ export async function fetchAvailableVersions(packageName: string) {
|
||||||
const metadata = await fetchAsJson(packageName);
|
const metadata = await fetchAsJson(packageName);
|
||||||
return Object.keys(metadata.versions);
|
return Object.keys(metadata.versions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchTarballUrl(packageName: string, version: string) {
|
||||||
|
const metadata = await fetchAsJson(packageName);
|
||||||
|
const versionMetadata = metadata.versions?.[version];
|
||||||
|
if (versionMetadata === undefined)
|
||||||
|
throw new Error(`${packageName}@${version} does not exist.`);
|
||||||
|
|
||||||
|
const {tarball} = versionMetadata.dist;
|
||||||
|
if (tarball === undefined || !tarball.startsWith(`http`))
|
||||||
|
throw new Error(`${packageName}@${version} does not have a valid tarball.`);
|
||||||
|
|
||||||
|
return tarball;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,12 @@ export interface PackageManagerSpec {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface InstallSpec {
|
||||||
|
location: string;
|
||||||
|
bin?: BinList | BinSpec;
|
||||||
|
hash: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data structure found in config.json
|
* The data structure found in config.json
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -758,3 +758,46 @@ it(`should be able to show the latest version`, async () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should download yarn classic from custom registry`, async () => {
|
||||||
|
await xfs.mktempPromise(async cwd => {
|
||||||
|
process.env.COREPACK_NPM_REGISTRY = `https://registry.npmmirror.com`;
|
||||||
|
process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT = `1`;
|
||||||
|
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||||
|
exitCode: 0,
|
||||||
|
stdout: /^1\.\d+\.\d+\r?\n$/,
|
||||||
|
stderr: /^Corepack is about to download https:\/\/registry\.npmmirror\.com\/yarn\/-\/yarn-1\.\d+\.\d+\.tgz\r?\n$/,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should keep working with cache
|
||||||
|
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||||
|
exitCode: 0,
|
||||||
|
stdout: /^1\.\d+\.\d+\r?\n$/,
|
||||||
|
stderr: ``,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should download yarn berry from custom registry`, async () => {
|
||||||
|
await xfs.mktempPromise(async cwd => {
|
||||||
|
process.env.COREPACK_NPM_REGISTRY = `https://registry.npmmirror.com`;
|
||||||
|
process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT = `1`;
|
||||||
|
|
||||||
|
await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), {
|
||||||
|
packageManager: `yarn@3.0.0`,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||||
|
exitCode: 0,
|
||||||
|
stdout: `3.0.0\n`,
|
||||||
|
stderr: `Corepack is about to download https://registry.npmmirror.com/@yarnpkg/cli-dist/-/cli-dist-3.0.0.tgz\n`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should keep working with cache
|
||||||
|
await expect(runCli(cwd, [`yarn`, `--version`])).resolves.toMatchObject({
|
||||||
|
exitCode: 0,
|
||||||
|
stdout: `3.0.0\n`,
|
||||||
|
stderr: ``,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
BIN
tests/nocks.db
BIN
tests/nocks.db
Binary file not shown.
Loading…
Reference in New Issue