Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,9 @@ same major line. Should you need to upgrade to a new major, use an explicit
(useful for commands like `yarn init`).

- `COREPACK_NPM_REGISTRY` sets the registry base url used when retrieving
package managers from npm. Default value is `https://registry.npmjs.org`
package managers from npm. Default value is `https://registry.npmjs.org`.
Tarball URLs hosted on `registry.npmjs.org` or `registry.yarnpkg.com` are
redirected to this registry, including Yarn Classic downloads.

- `COREPACK_NPM_TOKEN` sets a Bearer token authorization header when connecting
to a npm type registry.
Expand Down
12 changes: 2 additions & 10 deletions sources/corepackUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,19 +269,11 @@ export async function installVersion(installTarget: string, locator: Locator, {s
binPath = registry.bin;
}
}
url = url.replace(
npmRegistryUtils.DEFAULT_NPM_REGISTRY_URL,
() => process.env.COREPACK_NPM_REGISTRY!,
);
url = npmRegistryUtils.overrideRegistryURL(url);
}
} else {
url = decodeURIComponent(version);
if (process.env.COREPACK_NPM_REGISTRY && url.startsWith(npmRegistryUtils.DEFAULT_NPM_REGISTRY_URL)) {
url = url.replace(
npmRegistryUtils.DEFAULT_NPM_REGISTRY_URL,
() => process.env.COREPACK_NPM_REGISTRY!,
);
}
url = npmRegistryUtils.overrideRegistryURL(url);
}

debugUtils.log(`Installing ${locator.name}@${version} from ${url}`);
Expand Down
12 changes: 12 additions & 0 deletions sources/npmRegistryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ export const DEFAULT_HEADERS: Record<string, string> = {
};
export const DEFAULT_NPM_REGISTRY_URL = `https://registry.npmjs.org`;

export function overrideRegistryURL(url: string) {
if (!process.env.COREPACK_NPM_REGISTRY)
return url;

const parsedUrl = new URL(url);
if (parsedUrl.origin !== DEFAULT_NPM_REGISTRY_URL && parsedUrl.origin !== `https://registry.yarnpkg.com`)
return url;

const registry = process.env.COREPACK_NPM_REGISTRY.replace(/\/+$/, ``);
return `${registry}${parsedUrl.pathname}${parsedUrl.search}${parsedUrl.hash}`;
}

export async function fetchAsJson(packageName: string, version?: string) {
// Strip any trailing slashes so a `COREPACK_NPM_REGISTRY` with a trailing
// slash does not produce a double slash in the request URL (some registries,
Expand Down
2 changes: 1 addition & 1 deletion tests/_registryServer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ function generateVersionMetadata(packageName, version) {
dist: {
shasum,
size: mockPackageTarGz.length,
tarball: `https://registry.npmjs.org/${packageName}/-/${packageName}-${version}.tgz`,
tarball: `${process.env.TEST_TARBALL_REGISTRY || `https://registry.npmjs.org`}/${packageName}/-/${packageName}-${version}.tgz`,
...generateSignature(packageName, version),
},
};
Expand Down
7 changes: 4 additions & 3 deletions tests/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1883,7 +1883,8 @@ for (const authType of [`COREPACK_NPM_REGISTRY`, `COREPACK_NPM_TOKEN`, `COREPACK
process.env.COREPACK_INTEGRITY_KEYS = ``;
});

it(`should download yarn classic`, async () => {
it.each([`https://registry.npmjs.org`, `https://registry.yarnpkg.com`])(`should download yarn classic with tarballs on %s`, async registry => {
process.env.TEST_TARBALL_REGISTRY = registry;
await xfs.mktempPromise(async cwd => {
await expect(runCli(cwd, [`[email protected]`, `--version`], true)).resolves.toMatchObject({
exitCode: 0,
Expand Down Expand Up @@ -1917,9 +1918,9 @@ for (const authType of [`COREPACK_NPM_REGISTRY`, `COREPACK_NPM_TOKEN`, `COREPACK
});
});

it(`should download custom package manager`, async () => {
it.each([`https://registry.npmjs.org`, `https://registry.yarnpkg.com`])(`should download custom package manager from %s`, async registry => {
await xfs.mktempPromise(async cwd => {
await expect(runCli(cwd, [`customPkgManager@https://registry.npmjs.org/customPkgManager/-/customPkgManager-1.0.0.tgz`, `--version`], true)).resolves.toMatchObject({
await expect(runCli(cwd, [`customPkgManager@${registry}/customPkgManager/-/customPkgManager-1.0.0.tgz`, `--version`], true)).resolves.toMatchObject({
exitCode: 0,
stdout: `customPkgManager: Hello from custom registry\n`,
stderr: ``,
Expand Down
36 changes: 36 additions & 0 deletions tests/npmRegistryUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,45 @@ import {describe, beforeEach, it, expect, vi} from 'vitest';
import {fetchAsJson as httpFetchAsJson} from '../sources/httpUtils';
import {DEFAULT_HEADERS, DEFAULT_NPM_REGISTRY_URL} from '../sources/npmRegistryUtils';
import {fetchLatestStableVersion, fetchAsJson, verifySignature} from '../sources/npmRegistryUtils';
import {overrideRegistryURL} from '../sources/npmRegistryUtils';

vi.mock(`../sources/httpUtils.ts`);

describe(`npm registry utils overrideRegistryURL`, () => {
it.each([`https://registry.npmjs.org`, `https://registry.yarnpkg.com`])(`rewrites tarballs on %s to the configured registry`, registry => {
process.env.COREPACK_NPM_REGISTRY = `https://mirror.example.org/npm///`;

expect(overrideRegistryURL(`${registry}/yarn/-/yarn-1.22.22.tgz?download=1#sha224.abc`))
.toBe(`https://mirror.example.org/npm/yarn/-/yarn-1.22.22.tgz?download=1#sha224.abc`);
});

it(`preserves registry credentials and scoped package paths`, () => {
process.env.COREPACK_NPM_REGISTRY = `https://user:[email protected]/npm/`;

expect(overrideRegistryURL(`https://registry.yarnpkg.com/@yarnpkg/cli-dist/-/cli-dist-4.0.0.tgz`))
.toBe(`https://user:[email protected]/npm/@yarnpkg/cli-dist/-/cli-dist-4.0.0.tgz`);
});

it.each([
`https://registry.yarnpkg.com/yarn/-/yarn-1.22.22.tgz`,
`https://registry.npmjs.org/pnpm/-/pnpm-10.0.0.tgz`,
])(`preserves %s when no custom registry is configured`, url => {
expect(overrideRegistryURL(url)).toBe(url);
});

it.each([
`https://repo.yarnpkg.com/4.0.0/packages/yarnpkg-cli/bin/yarn.js`,
`https://cdn.example.org/yarn.tgz?registry=https://registry.yarnpkg.com`,
`https://registry.yarnpkg.com.example.org/yarn.tgz`,
`https://registry.npmjs.org.example.org/yarn.tgz`,
`https://example.org/https://registry.npmjs.org/yarn.tgz`,
])(`preserves URLs from other origins: %s`, url => {
process.env.COREPACK_NPM_REGISTRY = `https://mirror.example.org`;

expect(overrideRegistryURL(url)).toBe(url);
});
});

describe(`npm registry utils fetchAsJson`, () => {
beforeEach(() => {
vi.resetAllMocks();
Expand Down