Skip to content

Commit fe6a307

Browse files
merceyzaduh95
andauthored
feat!: use fetch (#365)
Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 65880ca commit fe6a307

File tree

99 files changed

+174
-403
lines changed

Some content is hidden

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

99 files changed

+174
-403
lines changed

.eslintrc.js

+7
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,11 @@ module.exports = {
22
extends: [
33
`@yarnpkg`,
44
],
5+
rules: {
6+
// eslint-disable-next-line @typescript-eslint/naming-convention
7+
'no-restricted-globals': [`error`, {
8+
name: `fetch`,
9+
message: `Use fetch from sources/httpUtils.ts`,
10+
}],
11+
},
512
};

.vscode/settings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
"eslint.nodePath": ".yarn/sdks",
88
"typescript.enablePromptUseWorkspaceTsdk": true,
99
"editor.codeActionsOnSave": {
10-
"source.fixAll.eslint": true
10+
"source.fixAll.eslint": "explicit"
1111
}
1212
}

package.json

+3-5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"@types/debug": "^4.1.5",
2626
"@types/jest": "^29.0.0",
2727
"@types/node": "^20.4.6",
28+
"@types/proxy-from-env": "^1",
2829
"@types/semver": "^7.1.0",
2930
"@types/tar": "^6.0.0",
3031
"@types/which": "^3.0.0",
@@ -40,13 +41,13 @@
4041
"eslint": "^8.0.0",
4142
"eslint-plugin-arca": "^0.16.0",
4243
"jest": "^29.0.0",
43-
"nock": "^13.0.4",
44-
"proxy-agent": "^6.2.2",
44+
"proxy-from-env": "^1.1.0",
4545
"semver": "^7.5.2",
4646
"supports-color": "^9.0.0",
4747
"tar": "^6.0.1",
4848
"ts-node": "^10.0.0",
4949
"typescript": "^5.0.4",
50+
"undici": "^6.4.0",
5051
"v8-compile-cache": "^2.3.0",
5152
"which": "^4.0.0"
5253
},
@@ -94,8 +95,5 @@
9495
"./shims/yarnpkg",
9596
"./shims/yarnpkg.ps1"
9697
]
97-
},
98-
"resolutions": {
99-
"vm2": "portal:./vm2"
10098
}
10199
}

sources/httpUtils.ts

+50-59
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
import {UsageError} from 'clipanion';
2-
import {once} from 'events';
3-
import type {RequestOptions} from 'https';
4-
import type {IncomingMessage, ClientRequest} from 'http';
5-
import {stderr, stdin} from 'process';
1+
import assert from 'assert';
2+
import {UsageError} from 'clipanion';
3+
import {once} from 'events';
4+
import {stderr, stdin} from 'process';
5+
import {Readable} from 'stream';
66

7-
export async function fetchUrlStream(url: string, options: RequestOptions = {}) {
7+
export async function fetch(input: string | URL, init?: RequestInit) {
88
if (process.env.COREPACK_ENABLE_NETWORK === `0`)
9-
throw new UsageError(`Network access disabled by the environment; can't reach ${url}`);
9+
throw new UsageError(`Network access disabled by the environment; can't reach ${input}`);
1010

11-
const {default: https} = await import(`https`);
12-
13-
const {ProxyAgent} = await import(`proxy-agent`);
14-
15-
const proxyAgent = new ProxyAgent();
11+
const agent = await getProxyAgent(input);
1612

1713
if (process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT === `1`) {
18-
console.error(`Corepack is about to download ${url}.`);
14+
console.error(`Corepack is about to download ${input}.`);
1915
if (stdin.isTTY && !process.env.CI) {
2016
stderr.write(`\nDo you want to continue? [Y/n] `);
2117
stdin.resume();
@@ -30,60 +26,55 @@ export async function fetchUrlStream(url: string, options: RequestOptions = {})
3026
}
3127
}
3228

33-
return new Promise<IncomingMessage>((resolve, reject) => {
34-
const createRequest = (url: string) => {
35-
const request: ClientRequest = https.get(url, {...options, agent: proxyAgent}, response => {
36-
const statusCode = response.statusCode;
37-
38-
if ([301, 302, 307, 308].includes(statusCode as number) && response.headers.location)
39-
return createRequest(response.headers.location as string);
40-
41-
if (statusCode != null && statusCode >= 200 && statusCode < 300)
42-
return resolve(response);
43-
44-
return reject(new Error(`Server answered with HTTP ${statusCode} when performing the request to ${url}; for troubleshooting help, see https://github.com/nodejs/corepack#troubleshooting`));
45-
});
29+
let response;
30+
try {
31+
response = await globalThis.fetch(input, {
32+
...init,
33+
dispatcher: agent,
34+
});
35+
} catch (error) {
36+
throw new Error(
37+
`Error when performing the request to ${input}; for troubleshooting help, see https://github.com/nodejs/corepack#troubleshooting`,
38+
{cause: error},
39+
);
40+
}
4641

47-
request.on(`error`, err => {
48-
reject(new Error(`Error when performing the request to ${url}; for troubleshooting help, see https://github.com/nodejs/corepack#troubleshooting`));
49-
});
50-
};
42+
if (!response.ok) {
43+
await response.arrayBuffer();
44+
throw new Error(
45+
`Server answered with HTTP ${response.status} when performing the request to ${input}; for troubleshooting help, see https://github.com/nodejs/corepack#troubleshooting`,
46+
);
47+
}
5148

52-
createRequest(url);
53-
});
49+
return response;
5450
}
5551

56-
export async function fetchAsBuffer(url: string, options?: RequestOptions) {
57-
const response = await fetchUrlStream(url, options);
58-
59-
return new Promise<Buffer>((resolve, reject) => {
60-
const chunks: Array<Buffer> = [];
52+
export async function fetchAsJson(input: string | URL, init?: RequestInit) {
53+
const response = await fetch(input, init);
54+
return response.json() as Promise<any>;
55+
}
6156

62-
response.on(`data`, chunk => {
63-
chunks.push(chunk);
64-
});
57+
export async function fetchUrlStream(input: string | URL, init?: RequestInit) {
58+
const response = await fetch(input, init);
59+
const webStream = response.body;
60+
assert(webStream, `Expected stream to be set`);
61+
const stream = Readable.fromWeb(webStream);
62+
return stream;
63+
}
6564

66-
response.on(`error`, error => {
67-
reject(error);
68-
});
65+
async function getProxyAgent(input: string | URL) {
66+
const {getProxyForUrl} = await import(`proxy-from-env`);
6967

70-
response.on(`end`, () => {
71-
resolve(Buffer.concat(chunks));
72-
});
73-
});
74-
}
68+
// @ts-expect-error - The internal implementation is compatible with a WHATWG URL instance
69+
const proxy = getProxyForUrl(input);
7570

76-
export async function fetchAsJson(url: string, options?: RequestOptions) {
77-
const buffer = await fetchAsBuffer(url, options);
78-
const asText = buffer.toString();
71+
if (!proxy) return undefined;
7972

80-
try {
81-
return JSON.parse(asText);
82-
} catch (error) {
83-
const truncated = asText.length > 30
84-
? `${asText.slice(0, 30)}...`
85-
: asText;
73+
// Doing a deep import here since undici isn't tree-shakeable
74+
const {default: ProxyAgent} = (await import(
75+
// @ts-expect-error No types for this specific file
76+
`undici/lib/proxy-agent.js`
77+
)) as { default: typeof import('undici').ProxyAgent };
8678

87-
throw new Error(`Couldn't parse JSON data: ${JSON.stringify(truncated)}`);
88-
}
79+
return new ProxyAgent(proxy);
8980
}

sources/npmRegistryUtils.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import {UsageError} from 'clipanion';
2-
import {OutgoingHttpHeaders} from 'http2';
1+
import {UsageError} from 'clipanion';
32

4-
import * as httpUtils from './httpUtils';
3+
import * as httpUtils from './httpUtils';
54

65
// load abbreviated metadata as that's all we need for these calls
76
// see: https://github.com/npm/registry/blob/cfe04736f34db9274a780184d1cdb2fb3e4ead2a/docs/responses/package-metadata.md
8-
export const DEFAULT_HEADERS: OutgoingHttpHeaders = {
7+
export const DEFAULT_HEADERS: Record<string, string> = {
98
[`Accept`]: `application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8`,
109
};
1110
export const DEFAULT_NPM_REGISTRY_URL = `https://registry.npmjs.org`;

tests/httpUtils.test.ts

-95
This file was deleted.

0 commit comments

Comments
 (0)