Skip to content

Commit ea9a68e

Browse files
authored
chore: replace axios with native node fetch (#4644)
1 parent c1fd988 commit ea9a68e

12 files changed

Lines changed: 816 additions & 183 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@
179179
"storybook": "8.6.17",
180180
"tapable": "2.3.0",
181181
"typedoc": "0.28.17",
182-
"undici": "7.18.2",
182+
"undici": "7.24.7",
183183
"unplugin": "3.0.0"
184184
},
185185
"devDependencies": {

packages/dts-plugin/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@
7171
"@module-federation/third-party-dts-extractor": "workspace:*",
7272
"adm-zip": "0.5.10",
7373
"ansi-colors": "4.1.3",
74-
"axios": "1.15.0",
7574
"isomorphic-ws": "5.0.0",
75+
"undici": "7.24.7",
7676
"node-schedule": "2.1.1",
7777
"ws": "8.18.0"
7878
},

packages/dts-plugin/src/core/lib/DTSManager.advance.spec.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import AdmZip from 'adm-zip';
2-
import axios from 'axios';
32
import dirTree from 'directory-tree';
43
import { readFileSync, rmSync } from 'fs';
54
import { join } from 'path';
65
import { describe, expect, it, vi, beforeAll } from 'vitest';
76
import { DTSManager } from './DTSManager';
7+
import * as utils from './utils';
88
const TEST_DIT_DIR = 'dist-test';
99

1010
describe('DTSManager advance usage', () => {
@@ -97,15 +97,20 @@ describe('DTSManager advance usage', () => {
9797
remoteOptions.typesFolder,
9898
);
9999
const apiFile = `${apiDistFolder}.d.ts`;
100-
// const prevAxiosGet = axios.get;
101-
axios.get = (url) => {
100+
vi.spyOn(utils, 'nativeFetch').mockImplementation((url) => {
102101
if (url.includes('.d.ts')) {
103-
return vi
104-
.fn()
105-
.mockResolvedValueOnce({ data: readFileSync(apiFile, 'utf8') })();
102+
return Promise.resolve({
103+
data: readFileSync(apiFile, 'utf8'),
104+
status: 200,
105+
headers: {},
106+
} as any);
106107
}
107-
return vi.fn().mockResolvedValueOnce({ data: zip.toBuffer() })();
108-
};
108+
return Promise.resolve({
109+
data: zip.toBuffer(),
110+
status: 200,
111+
headers: {},
112+
} as any);
113+
});
109114

110115
await dtsManager.consumeTypes();
111116

packages/dts-plugin/src/core/lib/DTSManager.general.spec.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import { describe, expect, it, vi, beforeEach } from 'vitest';
22
import { DTSManager } from './DTSManager';
3-
import axios from 'axios';
43
import fs from 'fs';
54
import path from 'path';
65
import { UpdateMode } from '../../server/constant';
76
import { ThirdPartyExtractor } from '@module-federation/third-party-dts-extractor';
87
import { HostOptions, RemoteInfo } from '../interfaces/HostOptions';
98
import { RemoteOptions } from '../interfaces/RemoteOptions';
109
import { downloadTypesArchive } from './archiveHandler';
10+
import * as utils from './utils';
1111

12-
vi.mock('axios');
1312
vi.mock('fs/promises');
1413
vi.mock('fs');
1514
vi.mock('./archiveHandler');
@@ -143,7 +142,11 @@ describe('DTSManager General Tests', () => {
143142
},
144143
};
145144

146-
vi.mocked(axios.get).mockResolvedValueOnce(manifestResponse);
145+
vi.spyOn(utils, 'nativeFetch').mockResolvedValueOnce({
146+
data: manifestResponse.data,
147+
status: 200,
148+
headers: {},
149+
} as any);
147150

148151
const remoteInfo: RemoteInfo = {
149152
name: 'test',
@@ -169,7 +172,11 @@ describe('DTSManager General Tests', () => {
169172
},
170173
};
171174

172-
vi.mocked(axios.get).mockResolvedValueOnce(manifestResponse);
175+
vi.spyOn(utils, 'nativeFetch').mockResolvedValueOnce({
176+
data: manifestResponse.data,
177+
status: 200,
178+
headers: {},
179+
} as any);
173180

174181
const remoteInfo: RemoteInfo = {
175182
name: 'test',
@@ -184,7 +191,9 @@ describe('DTSManager General Tests', () => {
184191
});
185192

186193
it('should handle manifest fetch errors', async () => {
187-
vi.mocked(axios.get).mockRejectedValueOnce(new Error('Network error'));
194+
vi.spyOn(utils, 'nativeFetch').mockRejectedValueOnce(
195+
new Error('Network error'),
196+
);
188197

189198
const remoteInfo: RemoteInfo = {
190199
name: 'test',
@@ -210,7 +219,11 @@ describe('DTSManager General Tests', () => {
210219
},
211220
};
212221

213-
vi.mocked(axios.get).mockResolvedValueOnce(manifestResponse);
222+
vi.spyOn(utils, 'nativeFetch').mockResolvedValueOnce({
223+
data: manifestResponse.data,
224+
status: 200,
225+
headers: {},
226+
} as any);
214227

215228
const remoteInfo: RemoteInfo = {
216229
name: 'test',
@@ -346,7 +359,11 @@ describe('DTSManager General Tests', () => {
346359
type PackageType<T> = T extends 'REMOTE_ALIAS_IDENTIFIER/Component' ? typeof import('REMOTE_ALIAS_IDENTIFIER/Component') : any;
347360
`;
348361

349-
vi.mocked(axios.get).mockResolvedValueOnce({ data: apiTypeContent });
362+
vi.spyOn(utils, 'nativeFetch').mockResolvedValueOnce({
363+
data: apiTypeContent,
364+
status: 200,
365+
headers: {},
366+
} as any);
350367
vi.spyOn(fs, 'writeFileSync');
351368

352369
// @ts-expect-error only need timeout, which is not required
@@ -390,7 +407,9 @@ describe('DTSManager General Tests', () => {
390407
apiTypeUrl: 'http://example.com/api.d.ts',
391408
};
392409

393-
vi.mocked(axios.get).mockRejectedValueOnce(new Error('Network error'));
410+
vi.spyOn(utils, 'nativeFetch').mockRejectedValueOnce(
411+
new Error('Network error'),
412+
);
394413
vi.spyOn(fs, 'writeFileSync');
395414

396415
await dtsManager.downloadAPITypes(remoteInfo, '/tmp/types');

packages/dts-plugin/src/core/lib/DTSManager.spec.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import AdmZip from 'adm-zip';
2-
import axios from 'axios';
32
import dirTree from 'directory-tree';
43
import { rmSync, existsSync } from 'fs';
54
import { join } from 'path';
65
import { describe, expect, it, vi, beforeAll } from 'vitest';
76
import { DTSManager } from './DTSManager';
87
import { UpdateMode } from '../../server/constant';
8+
import * as utils from './utils';
99

1010
const TEST_DIT_DIR = 'dist-test';
1111

@@ -326,7 +326,11 @@ describe('DTSManager', () => {
326326
const distFolder = join(projectRoot, TEST_DIT_DIR, typesFolder);
327327
const zip = new AdmZip();
328328
zip.addLocalFolder(distFolder);
329-
axios.get = vi.fn().mockResolvedValueOnce({ data: zip.toBuffer() });
329+
vi.spyOn(utils, 'nativeFetch').mockResolvedValueOnce({
330+
data: zip.toBuffer(),
331+
status: 200,
332+
headers: {},
333+
} as any);
330334
await dtsManager.consumeTypes();
331335

332336
expect(
@@ -337,7 +341,7 @@ describe('DTSManager', () => {
337341
});
338342

339343
it('no delete exist remote types if fetch new remote types failed', async () => {
340-
axios.get = vi.fn().mockRejectedValue(new Error('error'));
344+
vi.spyOn(utils, 'nativeFetch').mockRejectedValueOnce(new Error('error'));
341345
await dtsManager.consumeTypes();
342346
expect(
343347
dirTree(targetFolder, {
@@ -492,7 +496,11 @@ describe('DTSManager', () => {
492496
const distFolder = join(projectRoot, TEST_DIT_DIR, typesFolder);
493497
const zip = new AdmZip();
494498
zip.addLocalFolder(distFolder);
495-
axios.get = vi.fn().mockResolvedValueOnce({ data: zip.toBuffer() });
499+
vi.spyOn(utils, 'nativeFetch').mockResolvedValueOnce({
500+
data: zip.toBuffer(),
501+
status: 200,
502+
headers: {},
503+
} as any);
496504

497505
await dtsManager.updateTypes({
498506
remoteName: 'remote',

packages/dts-plugin/src/core/lib/DTSManager.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
HOST_API_TYPES_FILE_NAME,
2828
} from '../constant';
2929
import { fileLog, logger } from '../../server';
30-
import { axiosGet, cloneDeepOptions, isDebugMode } from './utils';
30+
import { nativeFetch, cloneDeepOptions, isDebugMode } from './utils';
3131
import { UpdateMode } from '../../server/constant';
3232

3333
export const MODULE_DTS_MANAGER_IDENTIFIER = 'MF DTS Manager';
@@ -222,7 +222,7 @@ class DTSManager {
222222
return remoteInfo as Required<RemoteInfo>;
223223
}
224224
const url = remoteInfo.url;
225-
const res = await axiosGet(url, {
225+
const res = await nativeFetch(url, {
226226
timeout: hostOptions.timeout,
227227
family: hostOptions.family,
228228
});
@@ -299,7 +299,7 @@ class DTSManager {
299299
}
300300
try {
301301
const url = apiTypeUrl;
302-
const res = await axiosGet(url, {
302+
const res = await nativeFetch(url, {
303303
timeout: hostOptions.timeout,
304304
family: hostOptions.family,
305305
});

packages/dts-plugin/src/core/lib/archiveHandler.test.ts

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ import type { RemoteOptions } from '../interfaces/RemoteOptions';
33
import type { TsConfigJson } from '../interfaces/TsConfigJson';
44

55
import AdmZip from 'adm-zip';
6-
import axios, { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
76
import {
87
existsSync,
98
mkdirSync,
109
mkdtempSync,
11-
readFileSync,
1210
rmSync,
1311
writeFileSync,
12+
readFileSync,
1413
} from 'fs';
1514
import os from 'os';
1615
import { join } from 'path';
@@ -31,6 +30,7 @@ import {
3130
retrieveTypesZipPath,
3231
} from './archiveHandler';
3332
import { fileLog } from '../../server';
33+
import * as utils from './utils';
3434

3535
describe('archiveHandler', () => {
3636
const tmpDir = mkdtempSync(join(os.tmpdir(), 'archive-handler'));
@@ -283,15 +283,13 @@ describe('archiveHandler', () => {
283283
Buffer.from('export declare const bar: number;'),
284284
);
285285

286-
const mockResponse: AxiosResponse<Buffer> = {
287-
data: zip.toBuffer(),
288-
status: 200,
289-
statusText: 'OK',
290-
headers: {},
291-
config: {} as InternalAxiosRequestConfig,
292-
request: {} as XMLHttpRequest,
293-
};
294-
vi.spyOn(axios, 'get').mockResolvedValueOnce(mockResponse);
286+
const nativeFetchMock = vi
287+
.spyOn(utils, 'nativeFetch')
288+
.mockResolvedValueOnce({
289+
data: zip.toBuffer(),
290+
status: 200,
291+
headers: {},
292+
} as any);
295293

296294
const result = await downloadTypesArchive(hostOptions)([
297295
destinationFolder,
@@ -300,38 +298,35 @@ describe('archiveHandler', () => {
300298

301299
expect(result).toEqual([destinationFolder, archivePath]);
302300
expect(existsSync(join(archivePath, 'sample.d.ts'))).toBeTruthy();
303-
expect(axios.get).toHaveBeenCalledTimes(1);
304-
// Only verify the URL and responseType
305-
const axiosGetMock = vi.mocked(axios.get);
306-
const [[url, options]] = axiosGetMock.mock.calls;
301+
expect(nativeFetchMock).toHaveBeenCalledTimes(1);
302+
const [[url, options]] = nativeFetchMock.mock.calls;
307303
expect(url).toBe(new URL(fileToDownload).href);
308304
expect(options.responseType).toBe('arraybuffer');
309305
});
310306

311307
it('should retry on network failure up to maxRetries', async () => {
312308
const error = new Error('Network error');
313-
vi.spyOn(axios, 'get').mockRejectedValue(error);
309+
const nativeFetchMock = vi
310+
.spyOn(utils, 'nativeFetch')
311+
.mockRejectedValue(error);
314312

315313
await expect(() =>
316314
downloadTypesArchive(hostOptions)([destinationFolder, fileToDownload]),
317315
).rejects.toThrowError(/Network error/);
318316

319-
expect(axios.get).toHaveBeenCalledTimes(hostOptions.maxRetries);
317+
expect(nativeFetchMock).toHaveBeenCalledTimes(hostOptions.maxRetries);
320318
});
321319

322320
it('should handle empty archives gracefully', async () => {
323321
const archivePath = join(tmpDir, destinationFolder);
324322
const zip = new AdmZip();
325323
// Add an empty directory to the archive
326324
zip.addFile('.keep', Buffer.from(''));
327-
vi.spyOn(axios, 'get').mockResolvedValueOnce({
325+
vi.spyOn(utils, 'nativeFetch').mockResolvedValueOnce({
328326
data: zip.toBuffer(),
329327
status: 200,
330-
statusText: 'OK',
331328
headers: {},
332-
config: {} as InternalAxiosRequestConfig,
333-
request: {} as XMLHttpRequest,
334-
} as AxiosResponse<Buffer>);
329+
} as any);
335330

336331
const result = await downloadTypesArchive(hostOptions)([
337332
destinationFolder,
@@ -349,14 +344,11 @@ describe('archiveHandler', () => {
349344

350345
const zip = new AdmZip();
351346
zip.addFile('new.d.ts', Buffer.from('new content'));
352-
vi.spyOn(axios, 'get').mockResolvedValueOnce({
347+
vi.spyOn(utils, 'nativeFetch').mockResolvedValueOnce({
353348
data: zip.toBuffer(),
354349
status: 200,
355-
statusText: 'OK',
356350
headers: {},
357-
config: {} as InternalAxiosRequestConfig,
358-
request: {} as XMLHttpRequest,
359-
} as AxiosResponse<Buffer>);
351+
} as any);
360352

361353
await downloadTypesArchive(hostOptions)([
362354
destinationFolder,
@@ -377,14 +369,11 @@ describe('archiveHandler', () => {
377369

378370
const zip = new AdmZip();
379371
zip.addFile('new.d.ts', Buffer.from('new content'));
380-
vi.spyOn(axios, 'get').mockResolvedValueOnce({
372+
vi.spyOn(utils, 'nativeFetch').mockResolvedValueOnce({
381373
data: zip.toBuffer(),
382374
status: 200,
383-
statusText: 'OK',
384375
headers: {},
385-
config: {} as InternalAxiosRequestConfig,
386-
request: {} as XMLHttpRequest,
387-
} as AxiosResponse<Buffer>);
376+
} as any);
388377

389378
await downloadTypesArchive(options)([destinationFolder, fileToDownload]);
390379

@@ -397,26 +386,25 @@ describe('archiveHandler', () => {
397386
...hostOptions,
398387
abortOnError: false,
399388
};
400-
vi.spyOn(axios, 'get').mockRejectedValue(new Error('Network error'));
389+
const nativeFetchMock = vi
390+
.spyOn(utils, 'nativeFetch')
391+
.mockRejectedValue(new Error('Network error'));
401392

402393
const result = await downloadTypesArchive(options)([
403394
destinationFolder,
404395
fileToDownload,
405396
]);
406397

407398
expect(result).toBeUndefined();
408-
expect(axios.get).toHaveBeenCalledTimes(options.maxRetries);
399+
expect(nativeFetchMock).toHaveBeenCalledTimes(options.maxRetries);
409400
});
410401

411402
it('should handle malformed zip data', async () => {
412-
vi.spyOn(axios, 'get').mockResolvedValueOnce({
403+
vi.spyOn(utils, 'nativeFetch').mockResolvedValueOnce({
413404
data: Buffer.from('not a valid zip file'),
414405
status: 200,
415-
statusText: 'OK',
416406
headers: {},
417-
config: {} as InternalAxiosRequestConfig,
418-
request: {} as XMLHttpRequest,
419-
} as AxiosResponse<Buffer>);
407+
} as any);
420408

421409
await expect(() =>
422410
downloadTypesArchive(hostOptions)([destinationFolder, fileToDownload]),

0 commit comments

Comments
 (0)