@mui/internal-bundle-size-checker 1.0.9-canary.5 → 1.0.9-canary.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/build/browser.d.ts +2 -0
- package/build/builder.d.ts +46 -0
- package/build/cli.d.ts +1 -0
- package/build/configLoader.d.ts +23 -0
- package/build/constants.d.ts +1 -0
- package/build/defineConfig.d.ts +8 -0
- package/build/fetchSnapshot.d.ts +7 -0
- package/build/fetchSnapshotWithFallback.d.ts +11 -0
- package/build/formatUtils.d.ts +6 -0
- package/build/git.d.ts +23 -0
- package/build/github.d.ts +2 -0
- package/build/index.d.ts +4 -0
- package/build/notifyPr.d.ts +12 -0
- package/build/renderMarkdownReport.d.ts +45 -0
- package/build/sizeDiff.d.ts +59 -0
- package/build/strings.d.ts +23 -0
- package/build/uploadSnapshot.d.ts +10 -0
- package/build/worker.d.ts +12 -0
- package/package.json +21 -27
- package/src/{viteBuilder.js → builder.js} +99 -34
- package/src/cli.js +169 -43
- package/src/configLoader.js +122 -43
- package/src/constants.js +1 -0
- package/src/fetchSnapshot.js +3 -61
- package/src/fetchSnapshotWithFallback.js +34 -0
- package/src/git.js +50 -0
- package/src/github.js +4 -1
- package/src/index.js +3 -9
- package/src/notifyPr.js +81 -0
- package/src/renderMarkdownReport.js +18 -24
- package/src/renderMarkdownReport.test.js +97 -80
- package/src/sizeDiff.js +1 -5
- package/src/strings.js +38 -0
- package/src/types.d.ts +12 -23
- package/src/uploadSnapshot.js +2 -2
- package/src/worker.js +13 -20
- package/tsconfig.build.json +15 -0
- package/tsconfig.json +2 -2
- package/src/webpackBuilder.js +0 -267
|
@@ -2,25 +2,22 @@
|
|
|
2
2
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
|
3
3
|
import { renderMarkdownReport } from './renderMarkdownReport.js';
|
|
4
4
|
import * as fetchSnapshotModule from './fetchSnapshot.js';
|
|
5
|
+
import * as fetchSnapshotWithFallbackModule from './fetchSnapshotWithFallback.js';
|
|
6
|
+
import * as gitModule from './git.js';
|
|
5
7
|
|
|
6
8
|
// Mock the fetchSnapshot module
|
|
7
9
|
vi.mock('./fetchSnapshot.js');
|
|
8
|
-
// Mock the
|
|
9
|
-
vi.mock('
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
compareCommits: vi.fn(),
|
|
13
|
-
listCommits: vi.fn(),
|
|
14
|
-
},
|
|
15
|
-
pulls: {
|
|
16
|
-
get: vi.fn(),
|
|
17
|
-
},
|
|
18
|
-
})),
|
|
19
|
-
}));
|
|
10
|
+
// Mock the fetchSnapshotWithFallback module
|
|
11
|
+
vi.mock('./fetchSnapshotWithFallback.js');
|
|
12
|
+
// Mock the git module
|
|
13
|
+
vi.mock('./git.js');
|
|
20
14
|
|
|
21
15
|
describe('renderMarkdownReport', () => {
|
|
22
16
|
const mockFetchSnapshot = vi.mocked(fetchSnapshotModule.fetchSnapshot);
|
|
23
|
-
const mockFetchSnapshotWithFallback = vi.mocked(
|
|
17
|
+
const mockFetchSnapshotWithFallback = vi.mocked(
|
|
18
|
+
fetchSnapshotWithFallbackModule.fetchSnapshotWithFallback,
|
|
19
|
+
);
|
|
20
|
+
const mockGetMergeBase = vi.mocked(gitModule.getMergeBase);
|
|
24
21
|
|
|
25
22
|
/** @type {PrInfo} */
|
|
26
23
|
const mockPrInfo = {
|
|
@@ -36,26 +33,13 @@ describe('renderMarkdownReport', () => {
|
|
|
36
33
|
},
|
|
37
34
|
};
|
|
38
35
|
|
|
39
|
-
beforeEach(
|
|
36
|
+
beforeEach(() => {
|
|
40
37
|
mockFetchSnapshot.mockClear();
|
|
41
38
|
mockFetchSnapshotWithFallback.mockClear();
|
|
39
|
+
mockGetMergeBase.mockClear();
|
|
42
40
|
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// Set up default mock for compareCommits to return the base commit SHA
|
|
47
|
-
vi.mocked(octokit.repos.compareCommits).mockResolvedValue(
|
|
48
|
-
/** @type {any} */ ({
|
|
49
|
-
data: {
|
|
50
|
-
merge_base_commit: {
|
|
51
|
-
sha: mockPrInfo.base.sha,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
}),
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
// Clear any previous mock calls
|
|
58
|
-
vi.mocked(octokit.repos.compareCommits).mockClear();
|
|
41
|
+
// Set up default mock for getMergeBase
|
|
42
|
+
mockGetMergeBase.mockResolvedValue(mockPrInfo.base.sha);
|
|
59
43
|
});
|
|
60
44
|
|
|
61
45
|
it('should generate markdown report with size increases', async () => {
|
|
@@ -264,39 +248,6 @@ describe('renderMarkdownReport', () => {
|
|
|
264
248
|
`);
|
|
265
249
|
});
|
|
266
250
|
|
|
267
|
-
it('should include CircleCI build number in details URL', async () => {
|
|
268
|
-
const baseSnapshot = {
|
|
269
|
-
'@mui/material/Button/index.js': { parsed: 15000, gzip: 4500 },
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const prSnapshot = {
|
|
273
|
-
'@mui/material/Button/index.js': { parsed: 15000, gzip: 4500 },
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
mockFetchSnapshotWithFallback.mockResolvedValueOnce({
|
|
277
|
-
snapshot: baseSnapshot,
|
|
278
|
-
actualCommit: 'abc123',
|
|
279
|
-
});
|
|
280
|
-
mockFetchSnapshot.mockResolvedValueOnce(prSnapshot);
|
|
281
|
-
|
|
282
|
-
const result = await renderMarkdownReport(mockPrInfo, '12345');
|
|
283
|
-
|
|
284
|
-
expect(result).toContain('circleCIBuildNumber=12345');
|
|
285
|
-
expect(result).toMatchInlineSnapshot(`
|
|
286
|
-
"**Total Size Change:** 0B<sup>(0.00%)</sup> - **Total Gzip Change:** 0B<sup>(0.00%)</sup>
|
|
287
|
-
Files: 1 total (0 added, 0 removed, 0 changed)
|
|
288
|
-
|
|
289
|
-
<details>
|
|
290
|
-
<summary>Show details for 1 more bundle</summary>
|
|
291
|
-
|
|
292
|
-
**@mui/material/Button/index.js** **parsed:** 0B<sup>(0.00%)</sup> **gzip:** 0B<sup>(0.00%)</sup>
|
|
293
|
-
|
|
294
|
-
</details>
|
|
295
|
-
|
|
296
|
-
[Details of bundle changes](https://frontend-public.mui.com/size-comparison/mui/material-ui/diff?prNumber=42&baseRef=master&baseCommit=abc123&headCommit=def456&circleCIBuildNumber=12345)"
|
|
297
|
-
`);
|
|
298
|
-
});
|
|
299
|
-
|
|
300
251
|
it('should handle no changes', async () => {
|
|
301
252
|
const baseSnapshot = {
|
|
302
253
|
'@mui/material/Button/index.js': { parsed: 15000, gzip: 4500 },
|
|
@@ -348,13 +299,13 @@ describe('renderMarkdownReport', () => {
|
|
|
348
299
|
});
|
|
349
300
|
mockFetchSnapshot.mockResolvedValueOnce(prSnapshot);
|
|
350
301
|
|
|
351
|
-
const result = await renderMarkdownReport(mockPrInfo,
|
|
302
|
+
const result = await renderMarkdownReport(mockPrInfo, {
|
|
352
303
|
track: ['@mui/material/Button/index.js', '@mui/material/TextField/index.js'],
|
|
353
304
|
});
|
|
354
305
|
|
|
355
306
|
expect(result).toMatchInlineSnapshot(`
|
|
356
|
-
"| Bundle | Parsed
|
|
357
|
-
|
|
307
|
+
"| Bundle | Parsed size | Gzip size |
|
|
308
|
+
|:----------|----------:|----------:|
|
|
358
309
|
| @mui/material/Button/index.js | 🔺+400B<sup>(+2.67%)</sup> | 🔺+100B<sup>(+2.22%)</sup> |
|
|
359
310
|
| @mui/material/TextField/index.js | 🔺+200B<sup>(+0.91%)</sup> | 🔺+100B<sup>(+1.54%)</sup> |
|
|
360
311
|
|
|
@@ -383,13 +334,13 @@ describe('renderMarkdownReport', () => {
|
|
|
383
334
|
});
|
|
384
335
|
mockFetchSnapshot.mockResolvedValueOnce(prSnapshot);
|
|
385
336
|
|
|
386
|
-
const result = await renderMarkdownReport(mockPrInfo,
|
|
337
|
+
const result = await renderMarkdownReport(mockPrInfo, {
|
|
387
338
|
track: ['@mui/material/Button/index.js', '@mui/material/TextField/index.js'],
|
|
388
339
|
});
|
|
389
340
|
|
|
390
341
|
expect(result).toMatchInlineSnapshot(`
|
|
391
|
-
"| Bundle | Parsed
|
|
392
|
-
|
|
342
|
+
"| Bundle | Parsed size | Gzip size |
|
|
343
|
+
|:----------|----------:|----------:|
|
|
393
344
|
| @mui/material/Button/index.js | 🔺+500B<sup>(+3.33%)</sup> | 🔺+150B<sup>(+3.33%)</sup> |
|
|
394
345
|
| @mui/material/TextField/index.js | 🔺+300B<sup>(+1.36%)</sup> | 🔺+150B<sup>(+2.31%)</sup> |
|
|
395
346
|
|
|
@@ -418,13 +369,13 @@ describe('renderMarkdownReport', () => {
|
|
|
418
369
|
});
|
|
419
370
|
mockFetchSnapshot.mockResolvedValueOnce(prSnapshot);
|
|
420
371
|
|
|
421
|
-
const result = await renderMarkdownReport(mockPrInfo,
|
|
372
|
+
const result = await renderMarkdownReport(mockPrInfo, {
|
|
422
373
|
track: ['@mui/material/Button/index.js'],
|
|
423
374
|
});
|
|
424
375
|
|
|
425
376
|
expect(result).toMatchInlineSnapshot(`
|
|
426
|
-
"| Bundle | Parsed
|
|
427
|
-
|
|
377
|
+
"| Bundle | Parsed size | Gzip size |
|
|
378
|
+
|:----------|----------:|----------:|
|
|
428
379
|
| @mui/material/Button/index.js | 🔺+400B<sup>(+2.67%)</sup> | 🔺+100B<sup>(+2.22%)</sup> |
|
|
429
380
|
|
|
430
381
|
|
|
@@ -452,13 +403,13 @@ describe('renderMarkdownReport', () => {
|
|
|
452
403
|
});
|
|
453
404
|
mockFetchSnapshot.mockResolvedValueOnce(prSnapshot);
|
|
454
405
|
|
|
455
|
-
const result = await renderMarkdownReport(mockPrInfo,
|
|
406
|
+
const result = await renderMarkdownReport(mockPrInfo, {
|
|
456
407
|
track: ['@mui/material/Button/index.js', '@mui/material/TextField/index.js'],
|
|
457
408
|
});
|
|
458
409
|
|
|
459
410
|
expect(result).toMatchInlineSnapshot(`
|
|
460
|
-
"| Bundle | Parsed
|
|
461
|
-
|
|
411
|
+
"| Bundle | Parsed size | Gzip size |
|
|
412
|
+
|:----------|----------:|----------:|
|
|
462
413
|
| @mui/material/Button/index.js | 0B<sup>(0.00%)</sup> | 0B<sup>(0.00%)</sup> |
|
|
463
414
|
| @mui/material/TextField/index.js | 0B<sup>(0.00%)</sup> | 0B<sup>(0.00%)</sup> |
|
|
464
415
|
|
|
@@ -485,13 +436,13 @@ describe('renderMarkdownReport', () => {
|
|
|
485
436
|
});
|
|
486
437
|
mockFetchSnapshot.mockResolvedValueOnce(prSnapshot);
|
|
487
438
|
|
|
488
|
-
const result = await renderMarkdownReport(mockPrInfo,
|
|
439
|
+
const result = await renderMarkdownReport(mockPrInfo, {
|
|
489
440
|
track: ['@mui/material/Button/index.js'],
|
|
490
441
|
});
|
|
491
442
|
|
|
492
443
|
expect(result).toMatchInlineSnapshot(`
|
|
493
|
-
"| Bundle | Parsed
|
|
494
|
-
|
|
444
|
+
"| Bundle | Parsed size | Gzip size |
|
|
445
|
+
|:----------|----------:|----------:|
|
|
495
446
|
| @mui/material/Button/index.js | 🔺+400B<sup>(+2.67%)</sup> | 🔺+100B<sup>(+2.22%)</sup> |
|
|
496
447
|
|
|
497
448
|
|
|
@@ -516,7 +467,7 @@ describe('renderMarkdownReport', () => {
|
|
|
516
467
|
mockFetchSnapshot.mockResolvedValueOnce(prSnapshot);
|
|
517
468
|
|
|
518
469
|
await expect(
|
|
519
|
-
renderMarkdownReport(mockPrInfo,
|
|
470
|
+
renderMarkdownReport(mockPrInfo, {
|
|
520
471
|
track: ['@mui/material/Button/index.js', '@mui/material/NonExistent/index.js'],
|
|
521
472
|
}),
|
|
522
473
|
).rejects.toThrow(
|
|
@@ -577,11 +528,77 @@ describe('renderMarkdownReport', () => {
|
|
|
577
528
|
});
|
|
578
529
|
mockFetchSnapshot.mockResolvedValueOnce(prSnapshot);
|
|
579
530
|
|
|
580
|
-
const result = await renderMarkdownReport(mockPrInfo,
|
|
531
|
+
const result = await renderMarkdownReport(mockPrInfo, { fallbackDepth: 1 });
|
|
581
532
|
|
|
582
533
|
expect(result).toContain(
|
|
583
534
|
'Using snapshot from parent commit parent1 (fallback from merge base abc123)',
|
|
584
535
|
);
|
|
585
536
|
expect(mockFetchSnapshotWithFallback).toHaveBeenCalledWith('mui/material-ui', 'abc123', 1);
|
|
586
537
|
});
|
|
538
|
+
|
|
539
|
+
it('should throw error when default getMergeBase fails', async () => {
|
|
540
|
+
const prSnapshot = {
|
|
541
|
+
'@mui/material/Button/index.js': { parsed: 15000, gzip: 4500 },
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
// Mock getMergeBase to fail (simulating no merge base found)
|
|
545
|
+
mockGetMergeBase.mockRejectedValue(new Error('fatal: Not a valid object name abc123'));
|
|
546
|
+
mockFetchSnapshot.mockResolvedValueOnce(prSnapshot);
|
|
547
|
+
|
|
548
|
+
await expect(renderMarkdownReport(mockPrInfo)).rejects.toThrow(
|
|
549
|
+
'fatal: Not a valid object name abc123',
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
// Verify that getMergeBase was called with correct parameters
|
|
553
|
+
expect(mockGetMergeBase).toHaveBeenCalledWith('abc123', 'def456');
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('should use custom getMergeBase function when provided', async () => {
|
|
557
|
+
const baseSnapshot = {
|
|
558
|
+
'@mui/material/Button/index.js': { parsed: 15000, gzip: 4500 },
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
const prSnapshot = {
|
|
562
|
+
'@mui/material/Button/index.js': { parsed: 15400, gzip: 4600 },
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const customGetMergeBase = vi.fn().mockResolvedValue('custom123');
|
|
566
|
+
|
|
567
|
+
// Reset mocks to ensure clean state for this test
|
|
568
|
+
mockFetchSnapshotWithFallback.mockReset();
|
|
569
|
+
mockFetchSnapshot.mockReset();
|
|
570
|
+
|
|
571
|
+
mockFetchSnapshotWithFallback.mockResolvedValueOnce({
|
|
572
|
+
snapshot: baseSnapshot,
|
|
573
|
+
actualCommit: 'custom123',
|
|
574
|
+
});
|
|
575
|
+
mockFetchSnapshot.mockResolvedValueOnce(prSnapshot);
|
|
576
|
+
|
|
577
|
+
const result = await renderMarkdownReport(mockPrInfo, { getMergeBase: customGetMergeBase });
|
|
578
|
+
|
|
579
|
+
// Verify that custom getMergeBase was called instead of default
|
|
580
|
+
expect(customGetMergeBase).toHaveBeenCalledWith('abc123', 'def456');
|
|
581
|
+
expect(mockGetMergeBase).not.toHaveBeenCalled();
|
|
582
|
+
expect(mockFetchSnapshotWithFallback).toHaveBeenCalledWith('mui/material-ui', 'custom123', 3);
|
|
583
|
+
|
|
584
|
+
expect(result).toContain('**Total Size Change:** 🔺+400B');
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it('should throw error when custom getMergeBase fails', async () => {
|
|
588
|
+
const prSnapshot = {
|
|
589
|
+
'@mui/material/Button/index.js': { parsed: 15000, gzip: 4500 },
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
const customGetMergeBase = vi.fn().mockRejectedValue(new Error('Custom merge base error'));
|
|
593
|
+
|
|
594
|
+
mockFetchSnapshot.mockResolvedValueOnce(prSnapshot);
|
|
595
|
+
|
|
596
|
+
await expect(
|
|
597
|
+
renderMarkdownReport(mockPrInfo, { getMergeBase: customGetMergeBase }),
|
|
598
|
+
).rejects.toThrow('Custom merge base error');
|
|
599
|
+
|
|
600
|
+
// Verify that custom getMergeBase was called
|
|
601
|
+
expect(customGetMergeBase).toHaveBeenCalledWith('abc123', 'def456');
|
|
602
|
+
expect(mockGetMergeBase).not.toHaveBeenCalled();
|
|
603
|
+
});
|
|
587
604
|
});
|
package/src/sizeDiff.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @description Represents a single bundle size entry
|
|
3
|
-
* @typedef {Object} SizeSnapshotEntry
|
|
4
|
-
* @property {number} parsed
|
|
5
|
-
* @property {number} gzip
|
|
6
|
-
*
|
|
7
2
|
* @description Represents a single bundle size snapshot
|
|
8
3
|
* @typedef {Object.<string, SizeSnapshotEntry>} SizeSnapshot
|
|
9
4
|
*
|
|
@@ -36,6 +31,7 @@
|
|
|
36
31
|
* @property {number} fileCounts.total - Total number of files
|
|
37
32
|
*/
|
|
38
33
|
|
|
34
|
+
/** @type {SizeSnapshotEntry} */
|
|
39
35
|
const nullSnapshot = { parsed: 0, gzip: 0 };
|
|
40
36
|
|
|
41
37
|
/**
|
package/src/strings.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const illegalRe = /[/?<>\\:*|"]/g;
|
|
2
|
+
// eslint-disable-next-line no-control-regex
|
|
3
|
+
const controlRe = /[\x00-\x1f\x80-\x9f]/g;
|
|
4
|
+
const reservedRe = /^\.+$/;
|
|
5
|
+
const windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
|
|
6
|
+
const windowsTrailingRe = /[. ]+$/;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Inspired by https://github.com/parshap/node-sanitize-filename
|
|
10
|
+
*
|
|
11
|
+
* Replaces characters in strings that are illegal/unsafe for filenames.
|
|
12
|
+
* Unsafe characters are either removed or replaced by a substitute set
|
|
13
|
+
* in the optional `options` object.
|
|
14
|
+
*
|
|
15
|
+
* Illegal Characters on Various Operating Systems
|
|
16
|
+
* / ? < > \ : * | "
|
|
17
|
+
* https://kb.acronis.com/content/39790
|
|
18
|
+
*
|
|
19
|
+
* Unicode Control codes
|
|
20
|
+
* C0 0x00-0x1f & C1 (0x80-0x9f)
|
|
21
|
+
* http://en.wikipedia.org/wiki/C0_and_C1_control_codes
|
|
22
|
+
*
|
|
23
|
+
* Reserved filenames on Unix-based systems (".", "..")
|
|
24
|
+
* Reserved filenames in Windows ("CON", "PRN", "AUX", "NUL", "COM1",
|
|
25
|
+
* "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
|
26
|
+
* "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", and
|
|
27
|
+
* "LPT9") case-insesitively and with or without filename extensions.
|
|
28
|
+
* @param {string} input
|
|
29
|
+
*/
|
|
30
|
+
export function escapeFilename(input, replacement = '_') {
|
|
31
|
+
const sanitized = input
|
|
32
|
+
.replace(illegalRe, replacement)
|
|
33
|
+
.replace(controlRe, replacement)
|
|
34
|
+
.replace(reservedRe, replacement)
|
|
35
|
+
.replace(windowsReservedRe, replacement)
|
|
36
|
+
.replace(windowsTrailingRe, replacement);
|
|
37
|
+
return sanitized;
|
|
38
|
+
}
|
package/src/types.d.ts
CHANGED
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
// WebpackEntry type
|
|
2
|
-
interface WebpackEntry {
|
|
3
|
-
import: string;
|
|
4
|
-
importName?: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
// Webpack stats types
|
|
8
|
-
interface StatsAsset {
|
|
9
|
-
name: string;
|
|
10
|
-
size: number;
|
|
11
|
-
related?: {
|
|
12
|
-
find: (predicate: (asset: any) => boolean) => { size: number; type: string };
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
interface StatsChunkGroup {
|
|
17
|
-
name: string;
|
|
18
|
-
assets: Array<{ name: string; size: number }>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
1
|
// Upload configuration with optional properties
|
|
22
2
|
interface UploadConfig {
|
|
23
3
|
repo?: string; // The repository name (e.g., "mui/material-ui")
|
|
@@ -41,6 +21,8 @@ interface ObjectEntry {
|
|
|
41
21
|
import?: string; // Optional package name to import
|
|
42
22
|
importedNames?: string[]; // Optional array of named imports
|
|
43
23
|
externals?: string[]; // Optional array of packages to exclude from the bundle
|
|
24
|
+
track?: boolean; // Whether this bundle should be tracked in PR comments (defaults to false)
|
|
25
|
+
expand?: boolean; // Whether to expand the entry to include all exports
|
|
44
26
|
}
|
|
45
27
|
|
|
46
28
|
type EntryPoint = StringEntry | ObjectEntry;
|
|
@@ -49,6 +31,8 @@ type EntryPoint = StringEntry | ObjectEntry;
|
|
|
49
31
|
interface BundleSizeCheckerConfigObject {
|
|
50
32
|
entrypoints: EntryPoint[];
|
|
51
33
|
upload?: UploadConfig | boolean | null;
|
|
34
|
+
comment?: boolean; // Whether to post PR comments (defaults to true)
|
|
35
|
+
replace?: Record<string, string>; // String replacements to apply during bundling
|
|
52
36
|
}
|
|
53
37
|
|
|
54
38
|
type BundleSizeCheckerConfig =
|
|
@@ -60,17 +44,18 @@ type BundleSizeCheckerConfig =
|
|
|
60
44
|
interface NormalizedBundleSizeCheckerConfig {
|
|
61
45
|
entrypoints: ObjectEntry[];
|
|
62
46
|
upload: NormalizedUploadConfig | null; // null means upload is disabled
|
|
47
|
+
comment: boolean; // Whether to post PR comments
|
|
48
|
+
replace: Record<string, string>; // String replacements to apply during bundling
|
|
63
49
|
}
|
|
64
50
|
|
|
65
51
|
// Command line argument types
|
|
66
52
|
interface CommandLineArgs {
|
|
67
53
|
analyze?: boolean;
|
|
68
|
-
accurateBundles?: boolean;
|
|
69
54
|
output?: string;
|
|
70
55
|
verbose?: boolean;
|
|
71
56
|
filter?: string[];
|
|
72
57
|
concurrency?: number;
|
|
73
|
-
|
|
58
|
+
debug?: boolean;
|
|
74
59
|
}
|
|
75
60
|
|
|
76
61
|
interface ReportCommandArgs {
|
|
@@ -91,7 +76,6 @@ interface DiffCommandArgs {
|
|
|
91
76
|
interface PrCommandArgs {
|
|
92
77
|
prNumber: number;
|
|
93
78
|
output?: 'json' | 'markdown';
|
|
94
|
-
circleci?: string;
|
|
95
79
|
}
|
|
96
80
|
|
|
97
81
|
interface PrInfo {
|
|
@@ -108,3 +92,8 @@ interface PrInfo {
|
|
|
108
92
|
sha: string;
|
|
109
93
|
};
|
|
110
94
|
}
|
|
95
|
+
|
|
96
|
+
interface SizeSnapshotEntry {
|
|
97
|
+
parsed: number;
|
|
98
|
+
gzip: number;
|
|
99
|
+
}
|
package/src/uploadSnapshot.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
1
|
+
import fs from 'node:fs';
|
|
2
2
|
import { S3Client, PutObjectCommand, PutObjectTaggingCommand } from '@aws-sdk/client-s3';
|
|
3
3
|
import { execa } from 'execa';
|
|
4
4
|
import { fromEnv } from '@aws-sdk/credential-providers';
|
|
@@ -20,7 +20,7 @@ async function getCurrentCommitSHA() {
|
|
|
20
20
|
*/
|
|
21
21
|
function sanitizeS3TagString(str) {
|
|
22
22
|
// Replace disallowed characters with underscore
|
|
23
|
-
const safe = str.replace(/[^a-zA-Z0-9
|
|
23
|
+
const safe = str.replace(/[^a-zA-Z0-9 +\-=.:/@]+/g, '_');
|
|
24
24
|
// Truncate to max lengths (256 for value)
|
|
25
25
|
const maxLen = 256;
|
|
26
26
|
return safe.length > maxLen ? safe.substring(0, maxLen) : safe;
|
package/src/worker.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import { pathToFileURL } from 'url';
|
|
2
|
-
import
|
|
3
|
-
import fs from 'fs/promises';
|
|
1
|
+
import { pathToFileURL } from 'node:url';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
4
3
|
import chalk from 'chalk';
|
|
5
|
-
import * as module from 'module';
|
|
4
|
+
import * as module from 'node:module';
|
|
6
5
|
import { byteSizeFormatter } from './formatUtils.js';
|
|
7
|
-
import {
|
|
8
|
-
import { getViteSizes } from './viteBuilder.js';
|
|
9
|
-
|
|
10
|
-
const require = module.createRequire(import.meta.url);
|
|
6
|
+
import { getBundleSizes } from './builder.js';
|
|
11
7
|
|
|
12
8
|
const rootDir = process.cwd();
|
|
13
9
|
|
|
@@ -21,11 +17,14 @@ async function getPeerDependencies(packageName) {
|
|
|
21
17
|
/** @type {string | undefined} */
|
|
22
18
|
let packageJsonPath;
|
|
23
19
|
|
|
20
|
+
const rootDirUrl = pathToFileURL(rootDir);
|
|
21
|
+
|
|
24
22
|
if (module.findPackageJSON) {
|
|
25
23
|
// findPackageJSON was added in: v23.2.0, v22.14.0
|
|
26
|
-
packageJsonPath = module.findPackageJSON(packageName, `${
|
|
24
|
+
packageJsonPath = module.findPackageJSON(packageName, `${rootDirUrl}/index.mjs`);
|
|
27
25
|
} else {
|
|
28
26
|
// Try to resolve packageName/package.json
|
|
27
|
+
const require = module.createRequire(`${rootDirUrl}/index.mjs`);
|
|
29
28
|
packageJsonPath = require.resolve(`${packageName}/package.json`, {
|
|
30
29
|
paths: [rootDir],
|
|
31
30
|
});
|
|
@@ -57,10 +56,10 @@ async function getPeerDependencies(packageName) {
|
|
|
57
56
|
|
|
58
57
|
/**
|
|
59
58
|
* Get sizes for a bundle
|
|
60
|
-
* @param {{ entry: ObjectEntry, args: CommandLineArgs, index: number, total: number }} options
|
|
61
|
-
* @returns {Promise<Array<[string,
|
|
59
|
+
* @param {{ entry: ObjectEntry, args: CommandLineArgs, index: number, total: number, replace?: Record<string, string> }} options
|
|
60
|
+
* @returns {Promise<Array<[string, SizeSnapshotEntry]>>}
|
|
62
61
|
*/
|
|
63
|
-
export default async function getSizes({ entry, args, index, total }) {
|
|
62
|
+
export default async function getSizes({ entry, args, index, total, replace }) {
|
|
64
63
|
// eslint-disable-next-line no-console -- process monitoring
|
|
65
64
|
console.log(chalk.blue(`Compiling ${index + 1}/${total}: ${chalk.bold(`[${entry.id}]`)}`));
|
|
66
65
|
|
|
@@ -83,12 +82,7 @@ export default async function getSizes({ entry, args, index, total }) {
|
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
try {
|
|
86
|
-
|
|
87
|
-
if (args.vite) {
|
|
88
|
-
sizeMap = await getViteSizes(entry, args);
|
|
89
|
-
} else {
|
|
90
|
-
sizeMap = await getWebpackSizes(entry, args);
|
|
91
|
-
}
|
|
85
|
+
const { sizes: sizeMap, treemapPath } = await getBundleSizes(entry, args, replace);
|
|
92
86
|
|
|
93
87
|
// Create a concise log message showing import details
|
|
94
88
|
let entryDetails = '';
|
|
@@ -112,9 +106,8 @@ export default async function getSizes({ entry, args, index, total }) {
|
|
|
112
106
|
${chalk.green('✓')} ${chalk.green.bold(`Completed ${index + 1}/${total}: [${entry.id}]`)}
|
|
113
107
|
${chalk.cyan('Import:')} ${entryDetails}
|
|
114
108
|
${chalk.cyan('Externals:')} ${entry.externals.join(', ')}
|
|
115
|
-
${chalk.cyan('Bundler:')} ${args.vite ? 'vite' : 'webpack'}
|
|
116
109
|
${chalk.cyan('Sizes:')} ${chalk.yellow(byteSizeFormatter.format(entrySize.parsed))} (${chalk.yellow(byteSizeFormatter.format(entrySize.gzip))} gzipped)
|
|
117
|
-
${args.analyze ? ` ${chalk.cyan('Analysis:')} ${chalk.underline(pathToFileURL(
|
|
110
|
+
${args.analyze ? ` ${chalk.cyan('Analysis:')} ${chalk.underline(pathToFileURL(treemapPath).href)}` : ''}
|
|
118
111
|
`.trim(),
|
|
119
112
|
);
|
|
120
113
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
// This config is for emitting declarations (.d.ts) only
|
|
3
|
+
// Actual .ts source files are transpiled via babel
|
|
4
|
+
"extends": "./tsconfig.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"paths": {},
|
|
7
|
+
"emitDeclarationOnly": true,
|
|
8
|
+
"outDir": "./build",
|
|
9
|
+
"rootDir": "./src",
|
|
10
|
+
"noEmit": false
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*"],
|
|
13
|
+
// removing "cli" since its going to run directly and not really imported anywhere
|
|
14
|
+
"exclude": ["**/*.spec.*", "**/*.test.*", "**/setupVitest.ts"]
|
|
15
|
+
}
|