@mui/internal-bundle-size-checker 1.0.9-canary.72 → 1.0.9-canary.74

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.
@@ -8,7 +8,6 @@ export type SyncPrCommentResult = {
8
8
  /**
9
9
  * Syncs a PR comment via the dashboard API.
10
10
  * @param {string} repo - Repository in owner/repo format
11
- * @param {Record<string, object>} sections - Section-specific parameters
12
11
  * @returns {Promise<SyncPrCommentResult>}
13
12
  */
14
- export declare function syncPrComment(repo: string, sections: Record<string, object>): Promise<SyncPrCommentResult>;
13
+ export declare function syncPrComment(repo: string): Promise<SyncPrCommentResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/internal-bundle-size-checker",
3
- "version": "1.0.9-canary.72",
3
+ "version": "1.0.9-canary.74",
4
4
  "description": "Bundle size checker for MUI packages.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,8 +27,6 @@
27
27
  }
28
28
  },
29
29
  "dependencies": {
30
- "@aws-sdk/client-s3": "^3.1008.0",
31
- "@aws-sdk/credential-providers": "^3.1008.0",
32
30
  "@octokit/rest": "^22.0.1",
33
31
  "chalk": "^5.6.2",
34
32
  "env-ci": "^11.2.0",
@@ -46,7 +44,7 @@
46
44
  "@types/micromatch": "4.0.10",
47
45
  "@types/yargs": "17.0.35"
48
46
  },
49
- "gitSha": "98e99bf28bdaf246aee9618320f28e440148e844",
47
+ "gitSha": "fcc57442eeb92486028b70bb43a2b7d09eaa7e52",
50
48
  "scripts": {
51
49
  "build": "tsgo -p tsconfig.build.json",
52
50
  "test": "pnpm -w test --project @mui/internal-bundle-size-checker",
package/src/ciReport.js CHANGED
@@ -25,7 +25,13 @@ const sizeSnapshotEntrySchema = z.object({
25
25
  gzip: z.number(),
26
26
  });
27
27
 
28
- const sizeSnapshotSchema = z.record(z.string(), sizeSnapshotEntrySchema);
28
+ const snapshotMetadataSchema = z.object({
29
+ trackedBundles: z.array(z.string()).optional(),
30
+ });
31
+
32
+ const sizeSnapshotSchema = z
33
+ .record(z.string(), sizeSnapshotEntrySchema)
34
+ .and(z.object({ _metadata: snapshotMetadataSchema }).partial());
29
35
 
30
36
  export const sizeSnapshotUploadSchema = ciReportUploadSchema(
31
37
  'size-snapshot',
package/src/cli.js CHANGED
@@ -93,45 +93,6 @@ async function getBundleSizes(args, config) {
93
93
  return sizeArrays.flat();
94
94
  }
95
95
 
96
- /**
97
- * Posts initial "in progress" PR comment via dashboard API.
98
- * Called for all CI builds — the server resolves the associated PR from
99
- * OIDC claims. If no PR exists yet (branch pushed before PR created),
100
- * the server returns a no-op response.
101
- * @param {NormalizedBundleSizeCheckerConfig} config - The loaded configuration
102
- * @returns {Promise<void>}
103
- */
104
- async function postInitialPrComment(config) {
105
- const ciInfo = getCiInfo();
106
-
107
- if (!ciInfo) {
108
- return;
109
- }
110
-
111
- if (!ciInfo.slug) {
112
- throw new Error('PR commenting enabled but repository information missing in CI build');
113
- }
114
-
115
- if (!config.upload) {
116
- throw new Error('PR commenting requires upload configuration to determine the API URL');
117
- }
118
-
119
- try {
120
- // eslint-disable-next-line no-console
121
- console.log('Posting initial PR comment via dashboard API...');
122
-
123
- const result = await syncPrComment(ciInfo.slug, { bundleSize: { status: 'pending' } });
124
-
125
- // eslint-disable-next-line no-console
126
- console.log(
127
- result.skipped ? 'No open PR found for this branch, skipping.' : 'Initial PR comment posted.',
128
- );
129
- } catch (/** @type {any} */ error) {
130
- console.error('Failed to post initial PR comment:', error.message);
131
- // Don't fail the build for comment failures
132
- }
133
- }
134
-
135
96
  /**
136
97
  * Main runner function
137
98
  * @param {CommandLineArgs} argv - Command line arguments
@@ -143,19 +104,25 @@ async function run(argv) {
143
104
 
144
105
  const config = await loadConfig(rootDir);
145
106
 
146
- // Post initial PR comment if enabled and in CI environment
147
- if (config && config.comment) {
148
- await postInitialPrComment(config);
149
- }
150
-
151
107
  // eslint-disable-next-line no-console
152
108
  console.log(`Starting bundle size snapshot creation with ${concurrency} workers...`);
153
109
 
154
110
  const bundleSizes = await getBundleSizes(argv, config);
111
+ // Get tracked bundles from config
112
+ const trackedBundles = config.entrypoints
113
+ .filter((entry) => entry.track === true)
114
+ .map((entry) => entry.id);
115
+
155
116
  const sortedBundleSizes = Object.fromEntries(
156
117
  bundleSizes.sort((a, b) => a[0].localeCompare(b[0])),
157
118
  );
158
119
 
120
+ // Add metadata with tracked bundles to the snapshot
121
+ if (trackedBundles.length > 0) {
122
+ // eslint-disable-next-line no-underscore-dangle
123
+ sortedBundleSizes._metadata = /** @type {any} */ ({ trackedBundles });
124
+ }
125
+
159
126
  // Ensure output directory exists
160
127
  await fs.mkdir(path.dirname(snapshotDestPath), { recursive: true });
161
128
  await fs.writeFile(snapshotDestPath, JSON.stringify(sortedBundleSizes, null, 2));
@@ -169,11 +136,7 @@ async function run(argv) {
169
136
  if (config && config.upload) {
170
137
  try {
171
138
  // eslint-disable-next-line no-console
172
- console.log(
173
- config.upload.legacyUpload
174
- ? 'Uploading bundle size snapshot directly to S3 (legacy)...'
175
- : 'Uploading bundle size snapshot via dashboard API...',
176
- );
139
+ console.log(`Uploading bundle size snapshot via dashboard API at ${config.upload.apiUrl}...`);
177
140
  const { key } = await uploadSnapshot(snapshotDestPath, config.upload);
178
141
  // eslint-disable-next-line no-console
179
142
  console.log(`Bundle size snapshot uploaded to S3 with key: ${key}`);
@@ -210,17 +173,7 @@ async function run(argv) {
210
173
  // eslint-disable-next-line no-console
211
174
  console.log('Syncing PR comment via dashboard API...');
212
175
 
213
- // Get tracked bundles from config
214
- const trackedBundles = config.entrypoints
215
- .filter((entry) => entry.track === true)
216
- .map((entry) => entry.id);
217
-
218
- const result = await syncPrComment(ciInfo.slug, {
219
- bundleSize: {
220
- status: 'complete',
221
- trackedBundles: trackedBundles.length > 0 ? trackedBundles : undefined,
222
- },
223
- });
176
+ const result = await syncPrComment(ciInfo.slug);
224
177
 
225
178
  // eslint-disable-next-line no-console
226
179
  console.log(
@@ -80,7 +80,10 @@ export function applyUploadConfigDefaults(uploadConfig, ciInfo) {
80
80
  throw new Error('Missing required field: upload.branch. Please specify a branch name.');
81
81
  }
82
82
 
83
- const legacyUpload = uploadConfig.legacyUpload ?? false;
83
+ const apiUrl =
84
+ uploadConfig.apiUrl ||
85
+ process.env.CI_REPORT_API_URL ||
86
+ 'https://code-infra-dashboard.onrender.com';
84
87
 
85
88
  // Return the normalized config
86
89
  /** @type {NormalizedUploadConfig} */
@@ -91,7 +94,7 @@ export function applyUploadConfigDefaults(uploadConfig, ciInfo) {
91
94
  uploadConfig.isPullRequest !== undefined
92
95
  ? Boolean(uploadConfig.isPullRequest)
93
96
  : Boolean(isPr),
94
- legacyUpload,
97
+ apiUrl,
95
98
  };
96
99
 
97
100
  // Add PR number from CI environment if available
@@ -211,6 +214,11 @@ async function normalizeEntries(entries, configPath) {
211
214
  ).flat();
212
215
 
213
216
  for (const entry of result) {
217
+ if (entry.id.startsWith('_')) {
218
+ throw new Error(
219
+ `Entry id "${entry.id}" must not start with "_". Ids starting with "_" are reserved for internal metadata.`,
220
+ );
221
+ }
214
222
  if (usedIds.has(entry.id)) {
215
223
  throw new Error(`Duplicate entry id found: "${entry.id}". Entry ids must be unique.`);
216
224
  }
@@ -9,10 +9,9 @@ const DEFAULT_API_URL = 'https://code-infra-dashboard.onrender.com';
9
9
  /**
10
10
  * Syncs a PR comment via the dashboard API.
11
11
  * @param {string} repo - Repository in owner/repo format
12
- * @param {Record<string, object>} sections - Section-specific parameters
13
12
  * @returns {Promise<SyncPrCommentResult>}
14
13
  */
15
- export async function syncPrComment(repo, sections) {
14
+ export async function syncPrComment(repo) {
16
15
  const oidcToken = process.env.CIRCLE_OIDC_TOKEN_V2;
17
16
  if (!oidcToken) {
18
17
  throw new Error('CIRCLE_OIDC_TOKEN_V2 environment variable is required for PR comment sync');
@@ -24,7 +23,7 @@ export async function syncPrComment(repo, sections) {
24
23
  const response = await fetch(url, {
25
24
  method: 'POST',
26
25
  headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${oidcToken}` },
27
- body: JSON.stringify({ repo, sections }),
26
+ body: JSON.stringify({ repo }),
28
27
  });
29
28
 
30
29
  if (!response.ok) {
package/src/types.d.ts CHANGED
@@ -3,7 +3,7 @@ export interface UploadConfig {
3
3
  repo?: string; // The repository name (e.g., "mui/material-ui")
4
4
  branch?: string; // Optional branch name (defaults to current Git branch)
5
5
  isPullRequest?: boolean; // Whether this is a pull request build (defaults to CI detection)
6
- legacyUpload?: boolean; // Upload directly to S3 instead of using the dashboard API
6
+ apiUrl?: string; // Dashboard API URL (defaults to https://code-infra-dashboard.onrender.com)
7
7
  }
8
8
 
9
9
  // Normalized upload configuration where all properties are defined
@@ -12,7 +12,7 @@ export interface NormalizedUploadConfig {
12
12
  branch: string; // Branch name
13
13
  isPullRequest: boolean; // Whether this is a pull request build
14
14
  prNumber?: string; // PR number (from CI environment)
15
- legacyUpload: boolean; // Whether to use direct S3 upload
15
+ apiUrl: string; // Dashboard API URL
16
16
  }
17
17
 
18
18
  // EntryPoint types
@@ -1,7 +1,5 @@
1
1
  import fs from 'node:fs';
2
- import { S3Client, PutObjectCommand, PutObjectTaggingCommand } from '@aws-sdk/client-s3';
3
2
  import { execa } from 'execa';
4
- import { fromEnv } from '@aws-sdk/credential-providers';
5
3
 
6
4
  /**
7
5
  * @typedef {import('./types.js').NormalizedUploadConfig} NormalizedUploadConfig
@@ -16,29 +14,15 @@ async function getCurrentCommitSHA() {
16
14
  return stdout.trim();
17
15
  }
18
16
 
19
- /**
20
- * Sanitizes a string to be used as an S3 tag value
21
- * See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-restrictions
22
- * @param {string} str
23
- * @returns {string}
24
- */
25
- function sanitizeS3TagString(str) {
26
- // Replace disallowed characters with underscore
27
- const safe = str.replace(/[^a-zA-Z0-9 +\-=.:/@]+/g, '_');
28
- // Truncate to max lengths (256 for value)
29
- const maxLen = 256;
30
- return safe.length > maxLen ? safe.substring(0, maxLen) : safe;
31
- }
32
-
33
17
  /**
34
18
  * Uploads the snapshot via the dashboard API (server-side proxied to S3).
19
+ * @param {string} apiUrl - Base URL of the CI report API
35
20
  * @param {Buffer} fileContent - The file content to upload
36
21
  * @param {NormalizedUploadConfig} uploadConfig - The normalized upload configuration
37
22
  * @param {string} sha - The commit SHA
38
23
  * @returns {Promise<{key:string}>}
39
24
  */
40
- async function uploadViaApi(fileContent, uploadConfig, sha) {
41
- const apiUrl = process.env.CI_REPORT_API_URL || 'https://code-infra-dashboard.onrender.com';
25
+ async function uploadViaApi(apiUrl, fileContent, uploadConfig, sha) {
42
26
  /** @type {import('./ciReport.js').SizeSnapshotUpload} */
43
27
  const requestBody = {
44
28
  version: 1,
@@ -78,53 +62,6 @@ async function uploadViaApi(fileContent, uploadConfig, sha) {
78
62
  return { key: result.key };
79
63
  }
80
64
 
81
- /**
82
- * Uploads the snapshot directly to S3 using AWS credentials.
83
- * @param {Buffer} fileContent - The file content to upload
84
- * @param {NormalizedUploadConfig} uploadConfig - The normalized upload configuration
85
- * @param {string} sha - The commit SHA
86
- * @returns {Promise<{key:string}>}
87
- */
88
- async function uploadDirectToS3(fileContent, uploadConfig, sha) {
89
- const { branch, isPullRequest } = uploadConfig;
90
-
91
- // Create S3 client (uses AWS credentials from environment)
92
- const client = new S3Client({
93
- region: process.env.AWS_REGION_ARTIFACTS || process.env.AWS_REGION || 'eu-central-1',
94
- credentials: fromEnv(),
95
- });
96
-
97
- // S3 bucket and key
98
- const bucket = 'mui-org-ci';
99
- const key = `artifacts/${uploadConfig.repo}/${sha}/size-snapshot.json`;
100
-
101
- // Upload the file first
102
- await client.send(
103
- new PutObjectCommand({
104
- Bucket: bucket,
105
- Key: key,
106
- Body: fileContent,
107
- ContentType: 'application/json',
108
- }),
109
- );
110
-
111
- // Then add tags to the uploaded object
112
- await client.send(
113
- new PutObjectTaggingCommand({
114
- Bucket: bucket,
115
- Key: key,
116
- Tagging: {
117
- TagSet: [
118
- { Key: 'isPullRequest', Value: isPullRequest ? 'yes' : 'no' },
119
- { Key: 'branch', Value: sanitizeS3TagString(branch) },
120
- ],
121
- },
122
- }),
123
- );
124
-
125
- return { key };
126
- }
127
-
128
65
  /**
129
66
  * Uploads the size snapshot to S3
130
67
  * @param {string} snapshotPath - The path to the size snapshot JSON file
@@ -139,9 +76,5 @@ export async function uploadSnapshot(snapshotPath, uploadConfig, commitSha) {
139
76
  fs.promises.readFile(snapshotPath),
140
77
  ]);
141
78
 
142
- if (uploadConfig.legacyUpload) {
143
- return uploadDirectToS3(fileContent, uploadConfig, sha);
144
- }
145
-
146
- return uploadViaApi(fileContent, uploadConfig, sha);
79
+ return uploadViaApi(uploadConfig.apiUrl, fileContent, uploadConfig, sha);
147
80
  }