From 335dc0bf5b41dbe2d744887629360c74ed083c25 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:53:37 +0200 Subject: [PATCH 01/18] [code-infra] CReate bundle size package --- .gitignore | 1 + bundle-size/bundle-size-checker.config.mjs | 51 ++++++++ bundle-size/package.json | 19 +++ dangerFileContent.ts | 3 +- package.json | 5 +- .../bundle-size-checker/.eslintrc.cjs | 14 +++ .../bin/bundle-size-checker.js | 5 + .../bundle-size-checker/configLoader.js | 66 ++++++++++ .../bundle-size-checker/create.js | 96 ++++++++++++++ .../bundle-size-checker/defineConfig.js | 15 +++ .../bundle-size-checker/index.js | 5 + .../bundle-size-checker}/loadComparison.js | 26 ++-- .../bundle-size-checker/package.json | 21 ++++ .../bundle-size-checker/worker.js | 118 ++++++++++-------- pnpm-lock.yaml | 116 +++++++++++++---- pnpm-workspace.yaml | 2 +- scripts/sizeSnapshot/.gitignore | 1 - scripts/sizeSnapshot/create.js | 69 ---------- scripts/sizeSnapshot/index.js | 3 - scripts/sizeSnapshot/package.json | 15 --- scripts/sizeSnapshot/worker.js | 62 --------- 21 files changed, 473 insertions(+), 240 deletions(-) create mode 100644 bundle-size/bundle-size-checker.config.mjs create mode 100644 bundle-size/package.json create mode 100644 packages-internal/bundle-size-checker/.eslintrc.cjs create mode 100755 packages-internal/bundle-size-checker/bin/bundle-size-checker.js create mode 100644 packages-internal/bundle-size-checker/configLoader.js create mode 100644 packages-internal/bundle-size-checker/create.js create mode 100644 packages-internal/bundle-size-checker/defineConfig.js create mode 100644 packages-internal/bundle-size-checker/index.js rename {scripts/sizeSnapshot => packages-internal/bundle-size-checker}/loadComparison.js (76%) create mode 100644 packages-internal/bundle-size-checker/package.json rename scripts/sizeSnapshot/webpack.config.js => packages-internal/bundle-size-checker/worker.js (58%) delete mode 100644 scripts/sizeSnapshot/.gitignore delete mode 100644 scripts/sizeSnapshot/create.js delete mode 100644 scripts/sizeSnapshot/index.js delete mode 100644 scripts/sizeSnapshot/package.json delete mode 100644 scripts/sizeSnapshot/worker.js diff --git a/.gitignore b/.gitignore index e0af26ad0d2a0b..b96f61af23f923 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ dist node_modules package-lock.json size-snapshot.json +bundle-sizes/ docs/public/static/blog/feed/* # vale downloaded config .github/styles/ diff --git a/bundle-size/bundle-size-checker.config.mjs b/bundle-size/bundle-size-checker.config.mjs new file mode 100644 index 00000000000000..a610bbf8ef9ea7 --- /dev/null +++ b/bundle-size/bundle-size-checker.config.mjs @@ -0,0 +1,51 @@ +/** + * @file Configuration file for bundle-size-checker + * + * This file determines which packages and components will have their bundle sizes measured. + */ +import path from 'path'; +import glob from 'fast-glob'; +import { defineConfig } from '@mui/internal-bundle-size-checker'; + +const rootDir = path.resolve(import.meta.dirname, '..'); + +/** + * Generates the entrypoints configuration by scanning the project structure. + */ +export default defineConfig(async () => { + // Discover Material UI components + const materialPackagePath = path.join(rootDir, 'packages/mui-material/build'); + const materialFiles = await glob(path.join(materialPackagePath, '([A-Z])*/index.js')); + const materialComponents = materialFiles.map((componentPath) => { + const componentName = path.basename(path.dirname(componentPath)); + return `@mui/material/${componentName}`; + }); + + // Discover Lab components + const labPackagePath = path.join(rootDir, 'packages/mui-lab/build'); + const labFiles = await glob(path.join(labPackagePath, '([A-Z])*/index.js')); + const labComponents = labFiles.map((componentPath) => { + const componentName = path.basename(path.dirname(componentPath)); + return `@mui/lab/${componentName}`; + }); + + // Return the complete entrypoints configuration + return { + entrypoints: [ + '@mui/material', + ...materialComponents, + '@mui/lab', + ...labComponents, + '@mui/private-theming', + '@mui/system', + '@mui/system/createBox', + '@mui/system/createStyled', + '@mui/material/styles#createTheme', + '@mui/system/colorManipulator', + '@mui/lab/useAutocomplete', + '@mui/material/useMediaQuery', + '@mui/material/useScrollTrigger', + '@mui/utils', + ], + }; +}); diff --git a/bundle-size/package.json b/bundle-size/package.json new file mode 100644 index 00000000000000..9a5cd3d47bbc5f --- /dev/null +++ b/bundle-size/package.json @@ -0,0 +1,19 @@ +{ + "name": "@mui/bundle-size", + "version": "1.0.0", + "private": true, + "description": "Bundle size measurement workspace for MUI packages", + "scripts": { + "check": "NODE_OPTIONS=\"--max-old-space-size=4096\" bundle-size-checker --output ../size-snapshot.json" + }, + "dependencies": { + "@mui/internal-bundle-size-checker": "workspace:*", + "@mui/material": "workspace:*", + "@mui/lab": "workspace:*", + "@mui/private-theming": "workspace:*", + "@mui/system": "workspace:*", + "@mui/utils": "workspace:*", + "fast-glob": "^3.3.2", + "path": "^0.12.7" + } +} diff --git a/dangerFileContent.ts b/dangerFileContent.ts index 89960978b0c16d..bfc8d7b8c11bb1 100644 --- a/dangerFileContent.ts +++ b/dangerFileContent.ts @@ -1,8 +1,7 @@ import { exec } from 'child_process'; import type * as dangerModule from 'danger'; import replaceUrl from '@mui-internal/api-docs-builder/utils/replaceUrl'; -// eslint-disable-next-line import/no-relative-packages -import { loadComparison } from './scripts/sizeSnapshot'; +import { loadComparison } from '@mui/internal-bundle-size-checker'; declare const danger: (typeof dangerModule)['danger']; declare const markdown: (typeof dangerModule)['markdown']; diff --git a/package.json b/package.json index d48330b2b05432..dbbfe9683ac723 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,8 @@ "valelint": "git ls-files | grep -h \".md$\" | xargs vale --filter='.Level==\"error\"'", "prettier": "pretty-quick --ignore-path .eslintignore --branch master", "prettier:all": "prettier --write . --ignore-path .eslintignore", - "size:snapshot": "node --max-old-space-size=4096 ./scripts/sizeSnapshot/create", - "size:why": "pnpm size:snapshot --analyze", + "size:snapshot": "pnpm --filter @mui/bundle-size check", + "size:why": "pnpm --filter @mui/bundle-size run check -- --analyze", "start": "pnpm install && pnpm docs:dev", "test": "node scripts/test.mjs", "tc": "nx run nx_test_tc", @@ -101,6 +101,7 @@ }, "dependencies": { "@googleapis/sheets": "^9.6.0", + "@mui/internal-bundle-size-checker": "workspace:*", "@netlify/functions": "^3.0.4", "@slack/bolt": "^4.2.1", "babel-plugin-transform-import-meta": "^2.3.2", diff --git a/packages-internal/bundle-size-checker/.eslintrc.cjs b/packages-internal/bundle-size-checker/.eslintrc.cjs new file mode 100644 index 00000000000000..41f130763b32d8 --- /dev/null +++ b/packages-internal/bundle-size-checker/.eslintrc.cjs @@ -0,0 +1,14 @@ +module.exports = { + rules: { + 'import/prefer-default-export': 'off', + // Allow .js file extensions in import statements for ESM compatibility + 'import/extensions': [ + 'error', + 'ignorePackages', + { + js: 'always', + mjs: 'always', + }, + ], + }, +}; diff --git a/packages-internal/bundle-size-checker/bin/bundle-size-checker.js b/packages-internal/bundle-size-checker/bin/bundle-size-checker.js new file mode 100755 index 00000000000000..d0aefb5b3f22ba --- /dev/null +++ b/packages-internal/bundle-size-checker/bin/bundle-size-checker.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +// @ts-check + +import '../create.js'; diff --git a/packages-internal/bundle-size-checker/configLoader.js b/packages-internal/bundle-size-checker/configLoader.js new file mode 100644 index 00000000000000..b533a3e8449819 --- /dev/null +++ b/packages-internal/bundle-size-checker/configLoader.js @@ -0,0 +1,66 @@ +/** + * Utility to load the bundle-size-checker configuration + */ + +import fs from 'fs'; +import path from 'path'; + +/** + * Attempts to load and parse a single config file + * @param {string} configPath - Path to the configuration file + * @returns {Promise<{entrypoints: string[]} | null>} The parsed config or null if file doesn't exist + * @throws {Error} If the file exists but has invalid format + */ +async function loadConfigFile(configPath) { + try { + if (!fs.existsSync(configPath)) { + return null; + } + + // Dynamic import for ESM + const configUrl = new URL(`file://${configPath}`); + const configModule = await import(configUrl.href); + let config = configModule.default; + + // Handle configs that might be Promise-returning functions + if (config instanceof Promise) { + config = await config; + } else if (typeof config === 'function') { + config = await config(); + } + + if (!config.entrypoints || !Array.isArray(config.entrypoints)) { + throw new Error('Configuration must include an entrypoints array'); + } + + return config; + } catch (error) { + console.error(`Error loading config from ${configPath}:`, error); + throw error; // Re-throw to indicate failure + } +} + +/** + * Attempts to load the config file from the given directory + * @param {string} rootDir - The directory to search for the config file + * @returns {Promise<{entrypoints: string[]}>} A promise that resolves to the config object + */ +export async function loadConfig(rootDir) { + const configPaths = [ + path.join(rootDir, 'bundle-size-checker.config.js'), + path.join(rootDir, 'bundle-size-checker.config.mjs'), + ]; + + for (const configPath of configPaths) { + // eslint-disable-next-line no-await-in-loop + const config = await loadConfigFile(configPath); + if (config) { + return config; + } + } + + // Error out if no config file exists + throw new Error( + 'No bundle-size-checker configuration file found. Please create a bundle-size-checker.config.js or bundle-size-checker.config.mjs file in your project root.', + ); +} diff --git a/packages-internal/bundle-size-checker/create.js b/packages-internal/bundle-size-checker/create.js new file mode 100644 index 00000000000000..49982e1d78b7bc --- /dev/null +++ b/packages-internal/bundle-size-checker/create.js @@ -0,0 +1,96 @@ +// @ts-check + +import path from 'path'; +import os from 'os'; +import fse from 'fs-extra'; +import yargs from 'yargs'; +import Piscina from 'piscina'; +import { loadConfig } from './configLoader.js'; + +const MAX_CONCURRENCY = Math.min(8, os.cpus().length); + +const rootDir = process.cwd(); + +/** + * creates size snapshot for every bundle that built with webpack + */ +async function getWebpackSizes(webpackEnvironment) { + const worker = new Piscina({ + filename: new URL('./worker.js', import.meta.url).href, + maxThreads: MAX_CONCURRENCY, + }); + // Clean and recreate the build directory + const buildDir = path.join(rootDir, 'build'); + await fse.emptyDir(buildDir); + + const config = await loadConfig(rootDir); + + if ( + !config || + !config.entrypoints || + !Array.isArray(config.entrypoints) || + config.entrypoints.length === 0 + ) { + throw new Error( + 'No valid configuration found. Create a bundle-size-checker.config.js or bundle-size-checker.config.mjs file with entrypoints array.', + ); + } + + const entries = config.entrypoints; + const uniqueEntries = new Set(entries); + + const sizeArrays = await Promise.all( + Array.from(uniqueEntries, (entry, index) => + worker.run({ entry, webpackEnvironment, index, total: uniqueEntries.size }), + ), + ); + + return sizeArrays.flat(); +} + +async function run(argv) { + const { analyze, accurateBundles, output } = argv; + + const snapshotDestPath = output ? path.resolve(output) : path.join(rootDir, 'size-snapshot.json'); + + const bundleSizes = Object.fromEntries([ + ...(await getWebpackSizes({ analyze, accurateBundles })), + ]); + + // Ensure output directory exists + await fse.mkdirp(path.dirname(snapshotDestPath)); + await fse.writeJSON(snapshotDestPath, bundleSizes, { spaces: 2 }); + + // eslint-disable-next-line no-console + console.log(`Bundle size snapshot written to ${snapshotDestPath}`); +} + +yargs(process.argv.slice(2)) + .command({ + command: '$0', + description: 'Saves a size snapshot in size-snapshot.json', + builder: (command) => { + return command + .option('analyze', { + default: false, + describe: 'Creates a webpack-bundle-analyzer report for each bundle.', + type: 'boolean', + }) + .option('accurateBundles', { + default: false, + describe: 'Displays used bundles accurately at the cost of more CPU cycles.', + type: 'boolean', + }) + .option('output', { + alias: 'o', + describe: + 'Path to output the size snapshot JSON file (defaults to size-snapshot.json in current directory).', + type: 'string', + }); + }, + handler: run, + }) + .help() + .strict(true) + .version(false) + .parse(); diff --git a/packages-internal/bundle-size-checker/defineConfig.js b/packages-internal/bundle-size-checker/defineConfig.js new file mode 100644 index 00000000000000..001448c864d760 --- /dev/null +++ b/packages-internal/bundle-size-checker/defineConfig.js @@ -0,0 +1,15 @@ +/** + * @typedef {Object} BundleSizeCheckerConfig + * @property {string[]} entrypoints - Array of entrypoints to check size for + */ + +/** + * Define a configuration for the bundle size checker. + * This is just a pass-through function for better TypeScript typing. + * + * @param {BundleSizeCheckerConfig} config - Configuration object + * @returns {BundleSizeCheckerConfig} The configuration object + */ +export default function defineConfig(config) { + return config; +} diff --git a/packages-internal/bundle-size-checker/index.js b/packages-internal/bundle-size-checker/index.js new file mode 100644 index 00000000000000..baaf1c394e5450 --- /dev/null +++ b/packages-internal/bundle-size-checker/index.js @@ -0,0 +1,5 @@ +import loadComparison from './loadComparison.js'; +import defineConfig from './defineConfig.js'; +import { loadConfig } from './configLoader.js'; + +export { loadComparison, defineConfig, loadConfig }; diff --git a/scripts/sizeSnapshot/loadComparison.js b/packages-internal/bundle-size-checker/loadComparison.js similarity index 76% rename from scripts/sizeSnapshot/loadComparison.js rename to packages-internal/bundle-size-checker/loadComparison.js index 8c128016d66213..f64b4a182f3b53 100644 --- a/scripts/sizeSnapshot/loadComparison.js +++ b/packages-internal/bundle-size-checker/loadComparison.js @@ -1,14 +1,17 @@ /** * `snapshots` always refer to size snapshots in this file */ -const path = require('path'); -const fse = require('fs-extra'); -const lodash = require('lodash'); +import path from 'path'; +import fse from 'fs-extra'; const ARTIFACT_SERVER = 'https://s3.eu-central-1.amazonaws.com/mui-org-ci'; -async function loadCurrentSnapshot() { - return fse.readJSON(path.join(__dirname, '../../size-snapshot.json')); +/** + * @param {string} [snapshotPath] - Optional path to the snapshot file + */ +async function loadCurrentSnapshot(snapshotPath) { + const filePath = snapshotPath || path.join(process.cwd(), 'size-snapshot.json'); + return fse.readJSON(filePath); } /** @@ -31,9 +34,14 @@ async function loadSnapshot(commitId, ref) { const nullSnapshot = { parsed: 0, gzip: 0 }; -module.exports = async function loadComparison(parentId, ref) { +/** + * @param {string} parentId - The commit ID + * @param {string} ref - The branch reference + * @param {string} [snapshotPath] - Optional path to the current snapshot file + */ +export default async function loadComparison(parentId, ref, snapshotPath) { const [currentSnapshot, previousSnapshot] = await Promise.all([ - loadCurrentSnapshot(), + loadCurrentSnapshot(snapshotPath), // continue non existing snapshots loadSnapshot(parentId, ref).catch((reason) => { console.warn(`Failed to load snapshot for ref '${ref}' and commit '${parentId}': `, reason); @@ -43,7 +51,7 @@ module.exports = async function loadComparison(parentId, ref) { const bundleKeys = Object.keys({ ...currentSnapshot, ...previousSnapshot }); - const bundles = lodash.fromPairs( + const bundles = Object.fromEntries( bundleKeys.map((bundle) => { // if a bundle was added the change should be +inf // if a bundle was removed the change should be -100% @@ -75,4 +83,4 @@ module.exports = async function loadComparison(parentId, ref) { current: 'HEAD', bundles, }; -}; +} diff --git a/packages-internal/bundle-size-checker/package.json b/packages-internal/bundle-size-checker/package.json new file mode 100644 index 00000000000000..634709b19fd663 --- /dev/null +++ b/packages-internal/bundle-size-checker/package.json @@ -0,0 +1,21 @@ +{ + "name": "@mui/internal-bundle-size-checker", + "version": "1.0.0", + "private": true, + "description": "Bundle size checker for MUI packages", + "type": "module", + "main": "./index.js", + "bin": { + "bundle-size-checker": "./bin/bundle-size-checker.js" + }, + "dependencies": { + "compression-webpack-plugin": "^10.0.0", + "fast-glob": "^3.3.2", + "fs-extra": "^11.2.0", + "piscina": "^4.2.1", + "terser-webpack-plugin": "^5.3.10", + "webpack": "^5.90.3", + "webpack-bundle-analyzer": "^4.10.1", + "yargs": "^17.7.2" + } +} diff --git a/scripts/sizeSnapshot/webpack.config.js b/packages-internal/bundle-size-checker/worker.js similarity index 58% rename from scripts/sizeSnapshot/webpack.config.js rename to packages-internal/bundle-size-checker/worker.js index 1cda40482f1953..116f56f2987dd1 100644 --- a/scripts/sizeSnapshot/webpack.config.js +++ b/packages-internal/bundle-size-checker/worker.js @@ -1,12 +1,14 @@ // @ts-check -const path = require('path'); -const CompressionPlugin = require('compression-webpack-plugin'); -const glob = require('fast-glob'); -const TerserPlugin = require('terser-webpack-plugin'); -const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); +import { promisify } from 'util'; +import path from 'path'; +import webpackCallbackBased from 'webpack'; +import CompressionPlugin from 'compression-webpack-plugin'; +import TerserPlugin from 'terser-webpack-plugin'; +import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; -const workspaceRoot = path.join(__dirname, '..', '..'); +const webpack = promisify(webpackCallbackBased); +const rootDir = process.cwd(); /** * @typedef {object} WebpackEntry @@ -19,50 +21,10 @@ const workspaceRoot = path.join(__dirname, '..', '..'); */ /** - * - * @returns {Promise} - */ -async function getWebpackEntries() { - const materialPackagePath = path.join(workspaceRoot, 'packages/mui-material/build'); - const materialComponents = (await glob(path.join(materialPackagePath, '([A-Z])*/index.js'))).map( - (componentPath) => { - const componentName = path.basename(path.dirname(componentPath)); - return `@mui/material/${componentName}`; - }, - ); - - const labPackagePath = path.join(workspaceRoot, 'packages/mui-lab/build'); - const labComponents = (await glob(path.join(labPackagePath, '([A-Z])*/index.js'))).map( - (componentPath) => { - const componentName = path.basename(path.dirname(componentPath)); - return `@mui/lab/${componentName}`; - }, - ); - - return [ - '@mui/material', - ...materialComponents, - '@mui/lab', - ...labComponents, - '@mui/styles', - '@mui/private-theming', - '@mui/system', - '@mui/system/createBox', - '@mui/system/createStyled', - '@mui/material/styles#createTheme', - '@mui/system/colorManipulator', - '@mui/lab/useAutocomplete', - '@mui/material/useMediaQuery', - '@mui/material/useScrollTrigger', - '@mui/utils', - ]; -} - -/** - * + * Creates webpack configuration for bundle size checking * @param {WebpackEntry} entry * @param {Environment} environment - * @returns + * @returns {import('webpack').Configuration} */ function createWebpackConfig(entry, environment) { const analyzerMode = environment.analyze ? 'static' : 'disabled'; @@ -105,7 +67,7 @@ function createWebpackConfig(entry, environment) { type: 'var', // type: 'module', }, - path: path.join(__dirname, 'build'), + path: path.join(rootDir, 'build'), }, plugins: [ new CompressionPlugin({ @@ -122,7 +84,7 @@ function createWebpackConfig(entry, environment) { }), ], // A context to the current dir, which has a node_modules folder with workspace dependencies - context: __dirname, + context: rootDir, entry: { // This format is a data: url combined with inline matchResource to obtain a virtual entry. // See https://github.com/webpack/webpack/issues/6437#issuecomment-874466638 @@ -137,5 +99,57 @@ function createWebpackConfig(entry, environment) { return configuration; } -exports.getWebpackEntries = getWebpackEntries; -exports.createWebpackConfig = createWebpackConfig; +export default async function getSizes({ entry, webpackEnvironment, index, total }) { + const sizes = []; + + const configuration = createWebpackConfig(entry, webpackEnvironment); + + // eslint-disable-next-line no-console -- process monitoring + console.log(`Compiling ${index + 1}/${total}: "${Object.keys(configuration.entry)}"`); + + const webpackStats = await webpack(configuration); + + if (webpackStats.hasErrors()) { + const { entrypoints, errors } = webpackStats.toJson({ + all: false, + entrypoints: true, + errors: true, + }); + throw new Error( + `The following errors occurred during bundling of ${Object.keys( + entrypoints, + )} with webpack: \n${errors + .map((error) => { + return `${JSON.stringify(error, null, 2)}`; + }) + .join('\n')}`, + ); + } + + const stats = webpackStats.toJson({ + all: false, + assets: true, + entrypoints: true, + relatedAssets: true, + }); + const assets = new Map(stats.assets.map((asset) => [asset.name, asset])); + + Object.values(stats.entrypoints).forEach((entrypoint) => { + let parsedSize = 0; + let gzipSize = 0; + + entrypoint.assets.forEach(({ name, size }) => { + const asset = assets.get(name); + const gzippedAsset = asset.related.find((relatedAsset) => { + return relatedAsset.type === 'gzipped'; + }); + + parsedSize += size; + gzipSize += gzippedAsset.size; + }); + + sizes.push([entrypoint.name, { parsed: parsedSize, gzip: gzipSize }]); + }); + + return sizes; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b0bc3040fed2dd..075c993a47193a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,6 +33,9 @@ importers: '@googleapis/sheets': specifier: ^9.6.0 version: 9.6.0(encoding@0.1.13) + '@mui/internal-bundle-size-checker': + specifier: workspace:* + version: link:packages-internal/bundle-size-checker '@netlify/functions': specifier: ^3.0.4 version: 3.0.4 @@ -635,6 +638,33 @@ importers: specifier: ^5.98.0 version: 5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) + bundle-size: + dependencies: + '@mui/internal-bundle-size-checker': + specifier: workspace:* + version: link:../packages-internal/bundle-size-checker + '@mui/lab': + specifier: workspace:* + version: link:../packages/mui-lab/build + '@mui/material': + specifier: workspace:* + version: link:../packages/mui-material/build + '@mui/private-theming': + specifier: workspace:* + version: link:../packages/mui-private-theming/build + '@mui/system': + specifier: workspace:* + version: link:../packages/mui-system/build + '@mui/utils': + specifier: workspace:* + version: link:../packages/mui-utils/build + fast-glob: + specifier: ^3.3.2 + version: 3.3.3 + path: + specifier: ^0.12.7 + version: 0.12.7 + docs: dependencies: '@babel/core': @@ -1040,6 +1070,36 @@ importers: specifier: ^1.20.6 version: 1.20.6 + packages-internal/bundle-size-checker: + dependencies: + compression-webpack-plugin: + specifier: ^10.0.0 + version: 10.0.0(webpack@5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))) + fast-glob: + specifier: ^3.3.2 + version: 3.3.3 + fs-extra: + specifier: ^11.2.0 + version: 11.3.0 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + piscina: + specifier: ^4.2.1 + version: 4.9.2 + terser-webpack-plugin: + specifier: ^5.3.10 + version: 5.3.14(webpack@5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))) + webpack: + specifier: ^5.90.3 + version: 5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) + webpack-bundle-analyzer: + specifier: ^4.10.1 + version: 4.10.2 + yargs: + specifier: ^17.7.2 + version: 17.7.2 + packages-internal/docs-utils: dependencies: rimraf: @@ -2153,30 +2213,6 @@ importers: packages/waterfall: {} - scripts/sizeSnapshot: - dependencies: - '@mui/joy': - specifier: workspace:^ - version: link:../../packages/mui-joy/build - '@mui/lab': - specifier: workspace:^ - version: link:../../packages/mui-lab/build - '@mui/material': - specifier: workspace:^ - version: link:../../packages/mui-material/build - '@mui/private-theming': - specifier: workspace:^ - version: link:../../packages/mui-private-theming/build - '@mui/styles': - specifier: latest - version: 6.4.8(@types/react@19.1.0)(react@19.1.0) - '@mui/system': - specifier: workspace:^ - version: link:../../packages/mui-system/build - '@mui/utils': - specifier: workspace:^ - version: link:../../packages/mui-utils/build - test: dependencies: '@react-spring/web': @@ -7204,6 +7240,12 @@ packages: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} + compression-webpack-plugin@10.0.0: + resolution: {integrity: sha512-wLXLIBwpul/ALcm7Aj+69X0pYT3BYt6DdPn3qrgBIh9YejV9Bju9ShhlAsjujLyWMo6SAweFIWaUoFmXZNuNrg==} + engines: {node: '>= 14.15.0'} + peerDependencies: + webpack: ^5.1.0 + compression-webpack-plugin@11.1.0: resolution: {integrity: sha512-zDOQYp10+upzLxW+VRSjEpRRwBXJdsb5lBMlRxx1g8hckIFBpe3DTI0en2w7h+beuq89576RVzfiXrkdPGrHhA==} engines: {node: '>= 18.12.0'} @@ -9082,6 +9124,9 @@ packages: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -11285,6 +11330,9 @@ packages: resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} engines: {node: '>=18'} + path@0.12.7: + resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -13293,6 +13341,9 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util@0.10.4: + resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} + util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} @@ -19532,6 +19583,12 @@ snapshots: dependencies: mime-db: 1.53.0 + compression-webpack-plugin@10.0.0(webpack@5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))): + dependencies: + schema-utils: 4.3.0 + serialize-javascript: 6.0.2 + webpack: 5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) + compression-webpack-plugin@11.1.0(webpack@5.98.0): dependencies: schema-utils: 4.3.0 @@ -21932,6 +21989,8 @@ snapshots: once: 1.4.0 wrappy: 1.0.2 + inherits@2.0.3: {} + inherits@2.0.4: {} ini@1.3.8: {} @@ -24749,6 +24808,11 @@ snapshots: path-type@6.0.0: {} + path@0.12.7: + dependencies: + process: 0.11.10 + util: 0.10.4 + pathe@2.0.3: {} pathval@1.1.1: {} @@ -27099,6 +27163,10 @@ snapshots: util-deprecate@1.0.2: {} + util@0.10.4: + dependencies: + inherits: 2.0.3 + util@0.12.5: dependencies: inherits: 2.0.4 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a678378dc094de..a7dc36534bae2e 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -7,6 +7,6 @@ packages: - test - test/moduleResolution - apps/* - - scripts/sizeSnapshot + - bundle-size patchedDependencies: styled-components: patches/styled-components.patch diff --git a/scripts/sizeSnapshot/.gitignore b/scripts/sizeSnapshot/.gitignore deleted file mode 100644 index d16386367f7cd7..00000000000000 --- a/scripts/sizeSnapshot/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build/ \ No newline at end of file diff --git a/scripts/sizeSnapshot/create.js b/scripts/sizeSnapshot/create.js deleted file mode 100644 index db5db30424827c..00000000000000 --- a/scripts/sizeSnapshot/create.js +++ /dev/null @@ -1,69 +0,0 @@ -// @ts-check - -const path = require('path'); -const os = require('os'); -const fse = require('fs-extra'); -const lodash = require('lodash'); -const yargs = require('yargs'); -const Piscina = require('piscina'); -const { getWebpackEntries } = require('./webpack.config'); - -const MAX_CONCURRENCY = Math.min(8, os.cpus().length); - -const workspaceRoot = path.join(__dirname, '../../'); -const snapshotDestPath = path.join(workspaceRoot, 'size-snapshot.json'); - -/** - * creates size snapshot for every bundle that built with webpack - */ -async function getWebpackSizes(webpackEnvironment) { - const worker = new Piscina({ - filename: require.resolve('./worker'), - maxThreads: MAX_CONCURRENCY, - }); - await fse.mkdirp(path.join(__dirname, 'build')); - - const entries = await getWebpackEntries(); - - const uniqueEntries = new Set(entries); - - const sizeArrays = await Promise.all( - Array.from(uniqueEntries, (entry, index) => - worker.run({ entry, webpackEnvironment, index, total: uniqueEntries.size }), - ), - ); - - return sizeArrays.flat(); -} - -async function run(argv) { - const { analyze, accurateBundles } = argv; - - const bundleSizes = lodash.fromPairs([...(await getWebpackSizes({ analyze, accurateBundles }))]); - - await fse.writeJSON(snapshotDestPath, bundleSizes, { spaces: 2 }); -} - -yargs - .command({ - command: '$0', - description: 'Saves a size snapshot in size-snapshot.json', - builder: (command) => { - return command - .option('analyze', { - default: false, - describe: 'Creates a webpack-bundle-analyzer report for each bundle.', - type: 'boolean', - }) - .option('accurateBundles', { - default: false, - describe: 'Displays used bundles accurately at the cost of more CPU cycles.', - type: 'boolean', - }); - }, - handler: run, - }) - .help() - .strict(true) - .version(false) - .parse(); diff --git a/scripts/sizeSnapshot/index.js b/scripts/sizeSnapshot/index.js deleted file mode 100644 index bd7dc7516776a1..00000000000000 --- a/scripts/sizeSnapshot/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const loadComparison = require('./loadComparison'); - -module.exports = { loadComparison }; diff --git a/scripts/sizeSnapshot/package.json b/scripts/sizeSnapshot/package.json deleted file mode 100644 index fef77f5a9f0d2d..00000000000000 --- a/scripts/sizeSnapshot/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "size-snapshot", - "version": "1.0.0", - "private": true, - "description": "Size snapshot of MUI packages", - "dependencies": { - "@mui/joy": "workspace:^", - "@mui/lab": "workspace:^", - "@mui/material": "workspace:^", - "@mui/private-theming": "workspace:^", - "@mui/styles": "latest", - "@mui/system": "workspace:^", - "@mui/utils": "workspace:^" - } -} diff --git a/scripts/sizeSnapshot/worker.js b/scripts/sizeSnapshot/worker.js deleted file mode 100644 index cd9d5e6864201e..00000000000000 --- a/scripts/sizeSnapshot/worker.js +++ /dev/null @@ -1,62 +0,0 @@ -const { promisify } = require('util'); -const webpackCallbackBased = require('webpack'); -const { createWebpackConfig } = require('./webpack.config'); - -const webpack = promisify(webpackCallbackBased); - -async function getSizes({ entry, webpackEnvironment, index, total }) { - const sizes = []; - - const configuration = createWebpackConfig(entry, webpackEnvironment); - - // eslint-disable-next-line no-console -- process monitoring - console.log(`Compiling ${index + 1}/${total}: "${Object.keys(configuration.entry)}"`); - - const webpackStats = await webpack(configuration); - - if (webpackStats.hasErrors()) { - const { entrypoints, errors } = webpackStats.toJson({ - all: false, - entrypoints: true, - errors: true, - }); - throw new Error( - `The following errors occurred during bundling of ${Object.keys( - entrypoints, - )} with webpack: \n${errors - .map((error) => { - return `${JSON.stringify(error, null, 2)}`; - }) - .join('\n')}`, - ); - } - - const stats = webpackStats.toJson({ - all: false, - assets: true, - entrypoints: true, - relatedAssets: true, - }); - const assets = new Map(stats.assets.map((asset) => [asset.name, asset])); - - Object.values(stats.entrypoints).forEach((entrypoint) => { - let parsedSize = 0; - let gzipSize = 0; - - entrypoint.assets.forEach(({ name, size }) => { - const asset = assets.get(name); - const gzippedAsset = asset.related.find((relatedAsset) => { - return relatedAsset.type === 'gzipped'; - }); - - parsedSize += size; - gzipSize += gzippedAsset.size; - }); - - sizes.push([entrypoint.name, { parsed: parsedSize, gzip: gzipSize }]); - }); - - return sizes; -} - -module.exports = getSizes; From c2da7686986f412ce174031a9e3ff27be28ce352 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Mon, 14 Apr 2025 16:26:28 +0200 Subject: [PATCH 02/18] fix up types --- CLAUDE.md | 36 +++++++ .../bin/bundle-size-checker.js | 2 - .../bundle-size-checker/create.js | 17 ++- .../bundle-size-checker/package.json | 8 ++ .../bundle-size-checker/tsconfig.json | 19 ++++ .../bundle-size-checker/types.d.ts | 35 ++++++ .../bundle-size-checker/worker.js | 101 +++++++++++------- pnpm-lock.yaml | 41 ++++++- 8 files changed, 212 insertions(+), 47 deletions(-) create mode 100644 CLAUDE.md create mode 100644 packages-internal/bundle-size-checker/tsconfig.json create mode 100644 packages-internal/bundle-size-checker/types.d.ts diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000000000..247bcb642c962b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,36 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +Always use `pnpm --filter ` to run pnpm commands in package context. + +- **Build**: `pnpm release:build` (builds all packages excluding docs). `pnpm --filter build` for individual packages. +- **Lint**: `pnpm eslint` (with caching), `pnpm prettier` (changed files) +- **Test**: `pnpm test` (runs ESLint, TypeScript check, and unit tests) +- **Single Test**: `pnpm tc [test name]` (runs a specific test) +- **TypeScript**: `pnpm typescript` (type checking) + +## Code Style + +- Use Prettier for formatting (100 char width, single quotes, trailing commas) +- Prefer `function Component()` over `React.FC` +- Name render functions in `React.forwardRef` for devtools +- Follow TypeScript conventions in TYPESCRIPT_CONVENTION.md +- Component props interfaces should be exported with name `{ComponentName}Props` +- For styled components, follow naming pattern `{ComponentName}{Slot}` +- Use proper JSDoc comments for public API components + +## Imports + +- Import from workspace with direct imports (e.g., from '@mui/material') +- Import test utilities from @mui/internal-test-utils + +## Whitelisted Domains + +- mui.com +- github.com +- npmjs.com +- react.dev +- developer.mozilla.org diff --git a/packages-internal/bundle-size-checker/bin/bundle-size-checker.js b/packages-internal/bundle-size-checker/bin/bundle-size-checker.js index d0aefb5b3f22ba..349b524e320693 100755 --- a/packages-internal/bundle-size-checker/bin/bundle-size-checker.js +++ b/packages-internal/bundle-size-checker/bin/bundle-size-checker.js @@ -1,5 +1,3 @@ #!/usr/bin/env node -// @ts-check - import '../create.js'; diff --git a/packages-internal/bundle-size-checker/create.js b/packages-internal/bundle-size-checker/create.js index 49982e1d78b7bc..655178898b558f 100644 --- a/packages-internal/bundle-size-checker/create.js +++ b/packages-internal/bundle-size-checker/create.js @@ -13,8 +13,10 @@ const rootDir = process.cwd(); /** * creates size snapshot for every bundle that built with webpack + * @param {CommandLineArgs} args + * @returns {Promise>} */ -async function getWebpackSizes(webpackEnvironment) { +async function getWebpackSizes(args) { const worker = new Piscina({ filename: new URL('./worker.js', import.meta.url).href, maxThreads: MAX_CONCURRENCY, @@ -41,13 +43,17 @@ async function getWebpackSizes(webpackEnvironment) { const sizeArrays = await Promise.all( Array.from(uniqueEntries, (entry, index) => - worker.run({ entry, webpackEnvironment, index, total: uniqueEntries.size }), + worker.run({ entry, args, index, total: uniqueEntries.size }), ), ); return sizeArrays.flat(); } +/** + * Main runner function + * @param {CommandLineArgs} argv - Command line arguments + */ async function run(argv) { const { analyze, accurateBundles, output } = argv; @@ -66,11 +72,12 @@ async function run(argv) { } yargs(process.argv.slice(2)) + // @ts-expect-error .command({ command: '$0', - description: 'Saves a size snapshot in size-snapshot.json', - builder: (command) => { - return command + describe: 'Saves a size snapshot in size-snapshot.json', + builder: (yargs) => { + return yargs .option('analyze', { default: false, describe: 'Creates a webpack-bundle-analyzer report for each bundle.', diff --git a/packages-internal/bundle-size-checker/package.json b/packages-internal/bundle-size-checker/package.json index 634709b19fd663..af667287c9c706 100644 --- a/packages-internal/bundle-size-checker/package.json +++ b/packages-internal/bundle-size-checker/package.json @@ -8,6 +8,9 @@ "bin": { "bundle-size-checker": "./bin/bundle-size-checker.js" }, + "scripts": { + "typescript": "tsc -p tsconfig.json" + }, "dependencies": { "compression-webpack-plugin": "^10.0.0", "fast-glob": "^3.3.2", @@ -17,5 +20,10 @@ "webpack": "^5.90.3", "webpack-bundle-analyzer": "^4.10.1", "yargs": "^17.7.2" + }, + "devDependencies": { + "@types/webpack": "^5.28.5", + "@types/webpack-bundle-analyzer": "^4.7.0", + "@types/yargs": "^17.0.33" } } diff --git a/packages-internal/bundle-size-checker/tsconfig.json b/packages-internal/bundle-size-checker/tsconfig.json new file mode 100644 index 00000000000000..c0ef407b7f6c80 --- /dev/null +++ b/packages-internal/bundle-size-checker/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "allowJs": true, + "checkJs": true, + "skipLibCheck": true, + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "isolatedModules": true, + "outDir": "./build", + "noEmit": true + }, + "include": ["**/*.js", "**/*.mjs", "**/*.ts"], + "exclude": ["node_modules", "build"] +} diff --git a/packages-internal/bundle-size-checker/types.d.ts b/packages-internal/bundle-size-checker/types.d.ts new file mode 100644 index 00000000000000..e9563ce451ffe4 --- /dev/null +++ b/packages-internal/bundle-size-checker/types.d.ts @@ -0,0 +1,35 @@ +// WebpackEntry type +interface WebpackEntry { + import: string; + importName?: string; +} + +// Webpack stats types +interface StatsAsset { + name: string; + size: number; + related?: { + find: (predicate: (asset: any) => boolean) => { size: number; type: string }; + }; +} + +interface StatsChunkGroup { + name: string; + assets: Array<{ name: string; size: number }>; +} + +interface WebpackStats { + hasErrors(): boolean; + toJson(options: any): { + assets?: StatsAsset[]; + entrypoints?: Record; + errors?: any[]; + }; +} + +// Command line argument types +interface CommandLineArgs { + analyze?: boolean; + accurateBundles?: boolean; + output?: string; +} diff --git a/packages-internal/bundle-size-checker/worker.js b/packages-internal/bundle-size-checker/worker.js index 116f56f2987dd1..a214b4c8c8b149 100644 --- a/packages-internal/bundle-size-checker/worker.js +++ b/packages-internal/bundle-size-checker/worker.js @@ -1,5 +1,3 @@ -// @ts-check - import { promisify } from 'util'; import path from 'path'; import webpackCallbackBased from 'webpack'; @@ -7,28 +5,24 @@ import CompressionPlugin from 'compression-webpack-plugin'; import TerserPlugin from 'terser-webpack-plugin'; import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; +/** + * @type {(options: webpackCallbackBased.Configuration) => Promise} + */ +// @ts-expect-error Can't select the right overload const webpack = promisify(webpackCallbackBased); const rootDir = process.cwd(); -/** - * @typedef {object} WebpackEntry - * @property {string} import - * @property {string} [importName] - * - * @typedef {object} Environment - * @property {boolean} analyze - * @property {boolean} accurateBundles - */ +// Type declarations are now in types.d.ts /** * Creates webpack configuration for bundle size checking - * @param {WebpackEntry} entry - * @param {Environment} environment + * @param {string} entry - Entry point string + * @param {CommandLineArgs} args * @returns {import('webpack').Configuration} */ -function createWebpackConfig(entry, environment) { - const analyzerMode = environment.analyze ? 'static' : 'disabled'; - const concatenateModules = !environment.accurateBundles; +function createWebpackConfig(entry, args) { + const analyzerMode = args.analyze ? 'static' : 'disabled'; + const concatenateModules = !args.accurateBundles; const entryName = entry; const [importSrc, importName] = entryName.split('#'); @@ -99,26 +93,39 @@ function createWebpackConfig(entry, environment) { return configuration; } -export default async function getSizes({ entry, webpackEnvironment, index, total }) { +/** + * Get sizes for a bundle + * @param {{ entry: string, args: CommandLineArgs, index: number, total: number }} options + * @returns {Promise>} + */ +export default async function getSizes({ entry, args, index, total }) { + /** @type {Array<[string, { parsed: number, gzip: number }]>} */ const sizes = []; - const configuration = createWebpackConfig(entry, webpackEnvironment); + const configuration = createWebpackConfig(entry, args); // eslint-disable-next-line no-console -- process monitoring - console.log(`Compiling ${index + 1}/${total}: "${Object.keys(configuration.entry)}"`); + console.log(`Compiling ${index + 1}/${total}: "${entry}"`); const webpackStats = await webpack(configuration); + if (!webpackStats) { + throw new Error('No webpack stats were returned'); + } + if (webpackStats.hasErrors()) { - const { entrypoints, errors } = webpackStats.toJson({ + const statsJson = webpackStats.toJson({ all: false, entrypoints: true, errors: true, }); + + const entrypointKeys = statsJson.entrypoints ? Object.keys(statsJson.entrypoints) : []; + throw new Error( - `The following errors occurred during bundling of ${Object.keys( - entrypoints, - )} with webpack: \n${errors + `The following errors occurred during bundling of ${entrypointKeys.join(', ')} with webpack: \n${( + statsJson.errors || [] + ) .map((error) => { return `${JSON.stringify(error, null, 2)}`; }) @@ -132,24 +139,44 @@ export default async function getSizes({ entry, webpackEnvironment, index, total entrypoints: true, relatedAssets: true, }); - const assets = new Map(stats.assets.map((asset) => [asset.name, asset])); - Object.values(stats.entrypoints).forEach((entrypoint) => { - let parsedSize = 0; - let gzipSize = 0; + if (!stats.assets) { + return sizes; + } - entrypoint.assets.forEach(({ name, size }) => { - const asset = assets.get(name); - const gzippedAsset = asset.related.find((relatedAsset) => { - return relatedAsset.type === 'gzipped'; - }); + const assets = new Map(stats.assets.map((asset) => [asset.name, asset])); - parsedSize += size; - gzipSize += gzippedAsset.size; + if (stats.entrypoints) { + Object.values(stats.entrypoints).forEach((entrypoint) => { + let parsedSize = 0; + let gzipSize = 0; + + if (entrypoint.assets) { + entrypoint.assets.forEach(({ name, size }) => { + const asset = assets.get(name); + if (asset && asset.related) { + const gzippedAsset = asset.related.find((relatedAsset) => { + return relatedAsset.type === 'gzipped'; + }); + + if (size !== undefined) { + parsedSize += size; + } + + if (gzippedAsset && gzippedAsset.size !== undefined) { + gzipSize += gzippedAsset.size; + } + } + }); + } + + if (!entrypoint.name) { + throw new Error('Entrypoint name is undefined'); + } + + sizes.push([entrypoint.name, { parsed: parsedSize, gzip: gzipSize }]); }); - - sizes.push([entrypoint.name, { parsed: parsedSize, gzip: gzipSize }]); - }); + } return sizes; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 075c993a47193a..607bc83736b9e1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1081,9 +1081,6 @@ importers: fs-extra: specifier: ^11.2.0 version: 11.3.0 - lodash: - specifier: ^4.17.21 - version: 4.17.21 piscina: specifier: ^4.2.1 version: 4.9.2 @@ -1099,6 +1096,16 @@ importers: yargs: specifier: ^17.7.2 version: 17.7.2 + devDependencies: + '@types/webpack': + specifier: ^5.28.5 + version: 5.28.5(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) + '@types/webpack-bundle-analyzer': + specifier: ^4.7.0 + version: 4.7.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) + '@types/yargs': + specifier: ^17.0.33 + version: 17.0.33 packages-internal/docs-utils: dependencies: @@ -6020,6 +6027,12 @@ packages: '@types/webfontloader@1.6.38': resolution: {integrity: sha512-kUaF72Fv202suFx6yBrwXqeVRMx7hGtJTesyESZgn9sEPCUeDXm2p0SiyS1MTqW74nQP4p7JyrOCwZ7pNFns4w==} + '@types/webpack-bundle-analyzer@4.7.0': + resolution: {integrity: sha512-c5i2ThslSNSG8W891BRvOd/RoCjI2zwph8maD22b1adtSns20j+0azDDMCK06DiVrzTgnwiDl5Ntmu1YRJw8Sg==} + + '@types/webpack@5.28.5': + resolution: {integrity: sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==} + '@types/webxr@0.5.21': resolution: {integrity: sha512-geZIAtLzjGmgY2JUi6VxXdCrTb99A7yP49lxLr2Nm/uIK0PkkxcEi4OGhoGDO4pxCf3JwGz2GiJL2Ej4K2bKaA==} @@ -18082,6 +18095,28 @@ snapshots: '@types/webfontloader@1.6.38': {} + '@types/webpack-bundle-analyzer@4.7.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))': + dependencies: + '@types/node': 20.17.30 + tapable: 2.2.1 + webpack: 5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + - webpack-cli + + '@types/webpack@5.28.5(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))': + dependencies: + '@types/node': 20.17.30 + tapable: 2.2.1 + webpack: 5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + - webpack-cli + '@types/webxr@0.5.21': {} '@types/ws@8.5.13': From 47e60cb730f7ff1f5cd10fe85be1dc07faee6dd1 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:32:48 +0200 Subject: [PATCH 03/18] Update create.js --- packages-internal/bundle-size-checker/create.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages-internal/bundle-size-checker/create.js b/packages-internal/bundle-size-checker/create.js index 655178898b558f..769bc98dd1c7e9 100644 --- a/packages-internal/bundle-size-checker/create.js +++ b/packages-internal/bundle-size-checker/create.js @@ -76,8 +76,8 @@ yargs(process.argv.slice(2)) .command({ command: '$0', describe: 'Saves a size snapshot in size-snapshot.json', - builder: (yargs) => { - return yargs + builder: (cmdYargs) => { + return cmdYargs .option('analyze', { default: false, describe: 'Creates a webpack-bundle-analyzer report for each bundle.', From 7efa56b8c8ab2a4c9085c62f779ffcc46cf1370d Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Mon, 14 Apr 2025 19:40:18 +0200 Subject: [PATCH 04/18] Update CLAUDE.md --- CLAUDE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 247bcb642c962b..cb97bc18ffdbb5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,3 +1,5 @@ + + # CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. From bc77a556227e061a5497d35520077e3dd143cdd7 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Fri, 18 Apr 2025 15:40:33 +0200 Subject: [PATCH 05/18] Update config.yml --- .circleci/config.yml | 73 +++++++++++++------------------------------- 1 file changed, 21 insertions(+), 52 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 293f1d1ab517f9..55297dc143c35e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -654,34 +654,18 @@ jobs: name: build @mui packages command: pnpm lerna run --ignore @mui/icons-material --concurrency 6 --scope "@mui/*" build - run: - name: create @mui/material canary distributable + # We'll add a bucket lifecycle policy to delete objects tagged as "pull_request" after 30 days. + name: Determine S3 object tag based on branch command: | - cd packages/mui-material/build - npm version 0.0.0-canary.${CIRCLE_SHA1} --no-git-tag-version - npm pack - mv mui-material-0.0.0-canary.${CIRCLE_SHA1}.tgz ../../../mui-material.tgz - - when: - # don't run on PRs - condition: - not: - matches: - # "^pull/\d+" is not valid YAML - # "^pull/\\d+" matches neither 'pull/1' nor 'main' - # Note that we want to include 'pull/1', 'pull/1/head' and ''pull/1/merge' - pattern: '^pull/.+$' - value: << pipeline.git.branch >> - steps: - - aws-cli/setup: - aws_access_key_id: $AWS_ACCESS_KEY_ID_ARTIFACTS - aws_secret_access_key: $AWS_SECRET_ACCESS_KEY_ARTIFACTS - region: ${AWS_REGION_ARTIFACTS} - # Upload distributables to S3 - - aws-s3/copy: - from: mui-material.tgz - to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/ - - store_artifacts: - path: mui-material.tgz - destination: mui-material.tgz + if [[ "<< pipeline.git.branch >>" =~ ^pull/.+$ ]]; then + echo "export S3_OBJECT_TAG=pull_request" >> $BASH_ENV + else + echo "export S3_OBJECT_TAG=base" >> $BASH_ENV + fi + - aws-cli/setup: + aws_access_key_id: $AWS_ACCESS_KEY_ID_ARTIFACTS + aws_secret_access_key: $AWS_SECRET_ACCESS_KEY_ARTIFACTS + region: ${AWS_REGION_ARTIFACTS} - run: name: create a size snapshot command: pnpm size:snapshot @@ -689,31 +673,16 @@ jobs: name: persist size snapshot as pipeline artifact path: size-snapshot.json destination: size-snapshot.json - - when: - # don't run on PRs - condition: - not: - matches: - # "^pull/\d+" is not valid YAML - # "^pull/\\d+" matches neither 'pull/1' nor 'main' - # Note that we want to include 'pull/1', 'pull/1/head' and ''pull/1/merge' - pattern: '^pull/.+$' - value: << pipeline.git.branch >> - steps: - - aws-cli/setup: - aws_access_key_id: $AWS_ACCESS_KEY_ID_ARTIFACTS - aws_secret_access_key: $AWS_SECRET_ACCESS_KEY_ARTIFACTS - region: ${AWS_REGION_ARTIFACTS} - # persist size snapshot on S3 - - aws-s3/copy: - arguments: --content-type application/json - from: size-snapshot.json - to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/ - # symlink size-snapshot to latest - - aws-s3/copy: - arguments: --content-type application/json - from: size-snapshot.json - to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/latest/ + # persist size snapshot on S3 (TODO: remove once UI can work with the new format) + - aws-s3/copy: + arguments: --content-type application/json --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TAG}]" + from: size-snapshot.json + to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/ + # persist size snapshot on S3 (new format = ///size-snapshot.json) + - aws-s3/copy: + arguments: --content-type application/json --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TAG}]" + from: size-snapshot.json + to: s3://mui-org-ci/artifacts/$CIRCLE_PROJECT_REPONAME/$CIRCLE_SHA1/ - run: name: Run danger on PRs command: pnpm danger ci --fail-on-errors From 4ced693463a01b5775aaaeb5e7e31e872a1b9bbf Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Fri, 18 Apr 2025 15:51:03 +0200 Subject: [PATCH 06/18] Update pnpm-lock.yaml --- pnpm-lock.yaml | 104 ++++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d576289055965..a20f2171b74b5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -390,7 +390,7 @@ importers: version: link:../../packages/mui-utils/build next: specifier: latest - version: 15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: specifier: ^19.1.0 version: 19.1.0 @@ -400,7 +400,7 @@ importers: devDependencies: '@pigment-css/nextjs-plugin': specifier: 0.0.30 - version: 0.0.30(@types/react@19.1.1)(next@15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3)(webpack-sources@3.2.3) + version: 0.0.30(@types/react@19.1.1)(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3)(webpack-sources@3.2.3) '@types/node': specifier: ^20.17.30 version: 20.17.30 @@ -445,7 +445,7 @@ importers: version: link:../../packages/mui-utils/build next: specifier: latest - version: 15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: specifier: ^19.1.0 version: 19.1.0 @@ -455,7 +455,7 @@ importers: devDependencies: '@pigment-css/nextjs-plugin': specifier: 0.0.30 - version: 0.0.30(@types/react@19.1.1)(next@15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3)(webpack-sources@3.2.3) + version: 0.0.30(@types/react@19.1.1)(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3)(webpack-sources@3.2.3) '@types/node': specifier: ^20.17.30 version: 20.17.30 @@ -774,7 +774,7 @@ importers: version: 4.1.3 '@toolpad/core': specifier: ^0.14.0 - version: 0.14.0(2c7d935002a0d9ced40ad9314280642b) + version: 0.14.0(8a8bb589237d311af14b7a7279ebf338) autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.3) @@ -858,7 +858,7 @@ importers: version: 5.3.5(@mui/material@packages+mui-material+build)(@types/react@19.1.1)(react@19.1.0) next: specifier: ^15.3.0 - version: 15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) notistack: specifier: 3.0.2 version: 3.0.2(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -1551,7 +1551,7 @@ importers: version: 19.1.1 next: specifier: ^15.3.0 - version: 15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: specifier: ^19.1.0 version: 19.1.0 @@ -1725,7 +1725,7 @@ importers: version: 4.17.21 next: specifier: ^15.3.0 - version: 15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: specifier: ^19.1.0 version: 19.1.0 @@ -1926,7 +1926,7 @@ importers: version: 19.1.1 next: specifier: ^15.3.0 - version: 15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: specifier: ^19.1.0 version: 19.1.0 @@ -4745,56 +4745,56 @@ packages: resolution: {integrity: sha512-z6okREyK8in0486a22Oro0k+YsuyEjDXJt46FpgeOgXqKJ9ElM8QPll0iuLBkpbH33ENiNbIPLd1cuClRQnhiw==} engines: {node: '>=18.0.0'} - '@next/env@15.3.0': - resolution: {integrity: sha512-6mDmHX24nWlHOlbwUiAOmMyY7KELimmi+ed8qWcJYjqXeC+G6JzPZ3QosOAfjNwgMIzwhXBiRiCgdh8axTTdTA==} + '@next/env@15.3.1': + resolution: {integrity: sha512-cwK27QdzrMblHSn9DZRV+DQscHXRuJv6MydlJRpFSqJWZrTYMLzKDeyueJNN9MGd8NNiUKzDQADAf+dMLXX7YQ==} '@next/eslint-plugin-next@15.3.0': resolution: {integrity: sha512-511UUcpWw5GWTyKfzW58U2F/bYJyjLE9e3SlnGK/zSXq7RqLlqFO8B9bitJjumLpj317fycC96KZ2RZsjGNfBw==} - '@next/swc-darwin-arm64@15.3.0': - resolution: {integrity: sha512-PDQcByT0ZfF2q7QR9d+PNj3wlNN4K6Q8JoHMwFyk252gWo4gKt7BF8Y2+KBgDjTFBETXZ/TkBEUY7NIIY7A/Kw==} + '@next/swc-darwin-arm64@15.3.1': + resolution: {integrity: sha512-hjDw4f4/nla+6wysBL07z52Gs55Gttp5Bsk5/8AncQLJoisvTBP0pRIBK/B16/KqQyH+uN4Ww8KkcAqJODYH3w==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.3.0': - resolution: {integrity: sha512-m+eO21yg80En8HJ5c49AOQpFDq+nP51nu88ZOMCorvw3g//8g1JSUsEiPSiFpJo1KCTQ+jm9H0hwXK49H/RmXg==} + '@next/swc-darwin-x64@15.3.1': + resolution: {integrity: sha512-q+aw+cJ2ooVYdCEqZVk+T4Ni10jF6Fo5DfpEV51OupMaV5XL6pf3GCzrk6kSSZBsMKZtVC1Zm/xaNBFpA6bJ2g==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.3.0': - resolution: {integrity: sha512-H0Kk04ZNzb6Aq/G6e0un4B3HekPnyy6D+eUBYPJv9Abx8KDYgNMWzKt4Qhj57HXV3sTTjsfc1Trc1SxuhQB+Tg==} + '@next/swc-linux-arm64-gnu@15.3.1': + resolution: {integrity: sha512-wBQ+jGUI3N0QZyWmmvRHjXjTWFy8o+zPFLSOyAyGFI94oJi+kK/LIZFJXeykvgXUk1NLDAEFDZw/NVINhdk9FQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.3.0': - resolution: {integrity: sha512-k8GVkdMrh/+J9uIv/GpnHakzgDQhrprJ/FbGQvwWmstaeFG06nnAoZCJV+wO/bb603iKV1BXt4gHG+s2buJqZA==} + '@next/swc-linux-arm64-musl@15.3.1': + resolution: {integrity: sha512-IIxXEXRti/AulO9lWRHiCpUUR8AR/ZYLPALgiIg/9ENzMzLn3l0NSxVdva7R/VDcuSEBo0eGVCe3evSIHNz0Hg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@15.3.0': - resolution: {integrity: sha512-ZMQ9yzDEts/vkpFLRAqfYO1wSpIJGlQNK9gZ09PgyjBJUmg8F/bb8fw2EXKgEaHbCc4gmqMpDfh+T07qUphp9A==} + '@next/swc-linux-x64-gnu@15.3.1': + resolution: {integrity: sha512-bfI4AMhySJbyXQIKH5rmLJ5/BP7bPwuxauTvVEiJ/ADoddaA9fgyNNCcsbu9SlqfHDoZmfI6g2EjzLwbsVTr5A==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.3.0': - resolution: {integrity: sha512-RFwq5VKYTw9TMr4T3e5HRP6T4RiAzfDJ6XsxH8j/ZeYq2aLsBqCkFzwMI0FmnSsLaUbOb46Uov0VvN3UciHX5A==} + '@next/swc-linux-x64-musl@15.3.1': + resolution: {integrity: sha512-FeAbR7FYMWR+Z+M5iSGytVryKHiAsc0x3Nc3J+FD5NVbD5Mqz7fTSy8CYliXinn7T26nDMbpExRUI/4ekTvoiA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@15.3.0': - resolution: {integrity: sha512-a7kUbqa/k09xPjfCl0RSVAvEjAkYBYxUzSVAzk2ptXiNEL+4bDBo9wNC43G/osLA/EOGzG4CuNRFnQyIHfkRgQ==} + '@next/swc-win32-arm64-msvc@15.3.1': + resolution: {integrity: sha512-yP7FueWjphQEPpJQ2oKmshk/ppOt+0/bB8JC8svPUZNy0Pi3KbPx2Llkzv1p8CoQa+D2wknINlJpHf3vtChVBw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.3.0': - resolution: {integrity: sha512-vHUQS4YVGJPmpjn7r5lEZuMhK5UQBNBRSB+iGDvJjaNk649pTIcRluDWNb9siunyLLiu/LDPHfvxBtNamyuLTw==} + '@next/swc-win32-x64-msvc@15.3.1': + resolution: {integrity: sha512-3PMvF2zRJAifcRNni9uMk/gulWfWS+qVI/pagd+4yLF5bcXPZPPH2xlYRYOsUjmCJOXSTAC2PjRzbhsRzR2fDQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -10734,8 +10734,8 @@ packages: nested-error-stacks@2.1.1: resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} - next@15.3.0: - resolution: {integrity: sha512-k0MgP6BsK8cZ73wRjMazl2y2UcXj49ZXLDEgx6BikWuby/CN+nh81qFFI16edgd7xYpe/jj2OZEIwCoqnzz0bQ==} + next@15.3.1: + resolution: {integrity: sha512-8+dDV0xNLOgHlyBxP1GwHGVaNXsmp+2NhZEYrXr24GWLHtt27YrBPbPuHvzlhi7kZNYjeJNR93IF5zfFu5UL0g==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: @@ -16467,34 +16467,34 @@ snapshots: '@netlify/serverless-functions-api@1.36.0': {} - '@next/env@15.3.0': {} + '@next/env@15.3.1': {} '@next/eslint-plugin-next@15.3.0': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@15.3.0': + '@next/swc-darwin-arm64@15.3.1': optional: true - '@next/swc-darwin-x64@15.3.0': + '@next/swc-darwin-x64@15.3.1': optional: true - '@next/swc-linux-arm64-gnu@15.3.0': + '@next/swc-linux-arm64-gnu@15.3.1': optional: true - '@next/swc-linux-arm64-musl@15.3.0': + '@next/swc-linux-arm64-musl@15.3.1': optional: true - '@next/swc-linux-x64-gnu@15.3.0': + '@next/swc-linux-x64-gnu@15.3.1': optional: true - '@next/swc-linux-x64-musl@15.3.0': + '@next/swc-linux-x64-musl@15.3.1': optional: true - '@next/swc-win32-arm64-msvc@15.3.0': + '@next/swc-win32-arm64-msvc@15.3.1': optional: true - '@next/swc-win32-x64-msvc@15.3.0': + '@next/swc-win32-x64-msvc@15.3.1': optional: true '@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3': @@ -16904,10 +16904,10 @@ snapshots: '@opentelemetry/api@1.8.0': optional: true - '@pigment-css/nextjs-plugin@0.0.30(@types/react@19.1.1)(next@15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3)(webpack-sources@3.2.3)': + '@pigment-css/nextjs-plugin@0.0.30(@types/react@19.1.1)(next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3)(webpack-sources@3.2.3)': dependencies: '@pigment-css/unplugin': 0.0.30(@types/react@19.1.1)(react@19.1.0)(typescript@5.8.3)(webpack-sources@3.2.3) - next: 15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@types/react' - react @@ -17762,7 +17762,7 @@ snapshots: '@theme-ui/css': 0.17.2(@emotion/react@11.13.5(@types/react@19.1.1)(react@19.1.0)) react: 19.1.0 - '@toolpad/core@0.14.0(2c7d935002a0d9ced40ad9314280642b)': + '@toolpad/core@0.14.0(8a8bb589237d311af14b7a7279ebf338)': dependencies: '@babel/runtime': 7.27.0 '@mui/icons-material': link:packages/mui-icons-material/build @@ -17782,7 +17782,7 @@ snapshots: prop-types: 15.8.1 react: 19.1.0 optionalDependencies: - next: 15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-router: 7.5.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@types/react' @@ -24089,9 +24089,9 @@ snapshots: nested-error-stacks@2.1.1: {} - next@15.3.0(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@15.3.1(@babel/core@7.26.10)(@opentelemetry/api@1.8.0)(@playwright/test@1.51.1)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@next/env': 15.3.0 + '@next/env': 15.3.1 '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 busboy: 1.6.0 @@ -24101,14 +24101,14 @@ snapshots: react-dom: 19.1.0(react@19.1.0) styled-jsx: 5.1.6(@babel/core@7.26.10)(babel-plugin-macros@3.1.0)(react@19.1.0) optionalDependencies: - '@next/swc-darwin-arm64': 15.3.0 - '@next/swc-darwin-x64': 15.3.0 - '@next/swc-linux-arm64-gnu': 15.3.0 - '@next/swc-linux-arm64-musl': 15.3.0 - '@next/swc-linux-x64-gnu': 15.3.0 - '@next/swc-linux-x64-musl': 15.3.0 - '@next/swc-win32-arm64-msvc': 15.3.0 - '@next/swc-win32-x64-msvc': 15.3.0 + '@next/swc-darwin-arm64': 15.3.1 + '@next/swc-darwin-x64': 15.3.1 + '@next/swc-linux-arm64-gnu': 15.3.1 + '@next/swc-linux-arm64-musl': 15.3.1 + '@next/swc-linux-x64-gnu': 15.3.1 + '@next/swc-linux-x64-musl': 15.3.1 + '@next/swc-win32-arm64-msvc': 15.3.1 + '@next/swc-win32-x64-msvc': 15.3.1 '@opentelemetry/api': 1.8.0 '@playwright/test': 1.51.1 sharp: 0.34.1 From 6f80625c155d6fd9d422712aa94584a9b4af9164 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Fri, 18 Apr 2025 16:05:35 +0200 Subject: [PATCH 07/18] Update config.yml --- .circleci/config.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 55297dc143c35e..e496c97f2d7a98 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -675,14 +675,28 @@ jobs: destination: size-snapshot.json # persist size snapshot on S3 (TODO: remove once UI can work with the new format) - aws-s3/copy: - arguments: --content-type application/json --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TAG}]" + arguments: --content-type application/json from: size-snapshot.json to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/ + - run: + name: Tag size-snapshot.json (legacy path) + command: | + aws s3api put-object-tagging \ + --bucket mui-org-ci \ + --key artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/size-snapshot.json \ + --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TAG}]" # persist size snapshot on S3 (new format = ///size-snapshot.json) - aws-s3/copy: - arguments: --content-type application/json --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TAG}]" + arguments: --content-type application/json from: size-snapshot.json to: s3://mui-org-ci/artifacts/$CIRCLE_PROJECT_REPONAME/$CIRCLE_SHA1/ + - run: + name: Tag size-snapshot.json (new path) + command: | + aws s3api put-object-tagging \ + --bucket mui-org-ci \ + --key artifacts/$CIRCLE_PROJECT_REPONAME/$CIRCLE_SHA1/size-snapshot.json \ + --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TAG}]" - run: name: Run danger on PRs command: pnpm danger ci --fail-on-errors From ab6b4fe2aa1c9b14f4251d49c2f0d5b037aa6300 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Fri, 18 Apr 2025 16:16:59 +0200 Subject: [PATCH 08/18] Update config.yml --- .circleci/config.yml | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e496c97f2d7a98..6e1abd494e39a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -655,13 +655,14 @@ jobs: command: pnpm lerna run --ignore @mui/icons-material --concurrency 6 --scope "@mui/*" build - run: # We'll add a bucket lifecycle policy to delete objects tagged as "pull_request" after 30 days. - name: Determine S3 object tag based on branch + name: Determine S3 object tags based on branch command: | if [[ "<< pipeline.git.branch >>" =~ ^pull/.+$ ]]; then - echo "export S3_OBJECT_TAG=pull_request" >> $BASH_ENV + echo "export S3_OBJECT_TYPE_TAG=pull_request" >> $BASH_ENV else - echo "export S3_OBJECT_TAG=base" >> $BASH_ENV + echo "export S3_OBJECT_TYPE_TAG=base" >> $BASH_ENV fi + echo "export S3_OBJECT_BRANCH_TAG=<< pipeline.git.branch >>" >> $BASH_ENV - aws-cli/setup: aws_access_key_id: $AWS_ACCESS_KEY_ID_ARTIFACTS aws_secret_access_key: $AWS_SECRET_ACCESS_KEY_ARTIFACTS @@ -669,34 +670,43 @@ jobs: - run: name: create a size snapshot command: pnpm size:snapshot + + # === LEGACY START === + # remove once the UI can handle the new format + # persist size snapshot on S3 + - when: + # don't run on PRs + condition: + not: + matches: + # "^pull/\d+" is not valid YAML + # "^pull/\\d+" matches neither 'pull/1' nor 'main' + # Note that we want to include 'pull/1', 'pull/1/head' and ''pull/1/merge' + pattern: '^pull/.+$' + value: << pipeline.git.branch >> + steps: + - aws-s3/copy: + arguments: --content-type application/json + from: size-snapshot.json + to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/ - store_artifacts: name: persist size snapshot as pipeline artifact path: size-snapshot.json destination: size-snapshot.json - # persist size snapshot on S3 (TODO: remove once UI can work with the new format) - - aws-s3/copy: - arguments: --content-type application/json - from: size-snapshot.json - to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/ - - run: - name: Tag size-snapshot.json (legacy path) - command: | - aws s3api put-object-tagging \ - --bucket mui-org-ci \ - --key artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/size-snapshot.json \ - --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TAG}]" + # === LEGACY END === + # persist size snapshot on S3 (new format = ///size-snapshot.json) - aws-s3/copy: arguments: --content-type application/json from: size-snapshot.json to: s3://mui-org-ci/artifacts/$CIRCLE_PROJECT_REPONAME/$CIRCLE_SHA1/ - run: - name: Tag size-snapshot.json (new path) + name: Tag size-snapshot.json command: | aws s3api put-object-tagging \ --bucket mui-org-ci \ --key artifacts/$CIRCLE_PROJECT_REPONAME/$CIRCLE_SHA1/size-snapshot.json \ - --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TAG}]" + --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TYPE_TAG},{Key=branch,Value=$S3_OBJECT_BRANCH_TAG}]" - run: name: Run danger on PRs command: pnpm danger ci --fail-on-errors From 5aeb6adb0d715581a8bf576d7b54c0aa86c0c472 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Fri, 18 Apr 2025 16:20:16 +0200 Subject: [PATCH 09/18] Update config.yml --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6e1abd494e39a5..19b07fc796745c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -699,13 +699,13 @@ jobs: - aws-s3/copy: arguments: --content-type application/json from: size-snapshot.json - to: s3://mui-org-ci/artifacts/$CIRCLE_PROJECT_REPONAME/$CIRCLE_SHA1/ + to: s3://mui-org-ci/artifacts/mui/material-ui/$CIRCLE_SHA1/ - run: name: Tag size-snapshot.json command: | aws s3api put-object-tagging \ --bucket mui-org-ci \ - --key artifacts/$CIRCLE_PROJECT_REPONAME/$CIRCLE_SHA1/size-snapshot.json \ + --key artifacts/mui/material-ui/$CIRCLE_SHA1/size-snapshot.json \ --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TYPE_TAG},{Key=branch,Value=$S3_OBJECT_BRANCH_TAG}]" - run: name: Run danger on PRs From 5b8bc79408c131b29e328f3a6bb984c1703cd5bc Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Fri, 18 Apr 2025 16:40:02 +0200 Subject: [PATCH 10/18] Update config.yml --- .circleci/config.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 19b07fc796745c..40aa3fead47aa8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -655,9 +655,9 @@ jobs: command: pnpm lerna run --ignore @mui/icons-material --concurrency 6 --scope "@mui/*" build - run: # We'll add a bucket lifecycle policy to delete objects tagged as "pull_request" after 30 days. - name: Determine S3 object tags based on branch + name: Determine S3 object tags based on whether this is a PR command: | - if [[ "<< pipeline.git.branch >>" =~ ^pull/.+$ ]]; then + if [[ -n "${CIRCLE_PULL_REQUEST}" ]]; then echo "export S3_OBJECT_TYPE_TAG=pull_request" >> $BASH_ENV else echo "export S3_OBJECT_TYPE_TAG=base" >> $BASH_ENV @@ -679,11 +679,8 @@ jobs: condition: not: matches: - # "^pull/\d+" is not valid YAML - # "^pull/\\d+" matches neither 'pull/1' nor 'main' - # Note that we want to include 'pull/1', 'pull/1/head' and ''pull/1/merge' - pattern: '^pull/.+$' - value: << pipeline.git.branch >> + pattern: ".+" + value: "${CIRCLE_PULL_REQUEST}" steps: - aws-s3/copy: arguments: --content-type application/json From 15e99f3940dc845e6a409ec32a9b3a3a47a916a1 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Fri, 18 Apr 2025 16:44:02 +0200 Subject: [PATCH 11/18] Update config.yml --- .circleci/config.yml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 40aa3fead47aa8..f6d8f471ac5f46 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -650,9 +650,18 @@ jobs: environment: DANGER_COMMAND: prepareBundleSizeReport - setup_corepack + - run: name: build @mui packages command: pnpm lerna run --ignore @mui/icons-material --concurrency 6 --scope "@mui/*" build + - run: + name: create a size snapshot + command: pnpm size:snapshot + + - aws-cli/setup: + aws_access_key_id: $AWS_ACCESS_KEY_ID_ARTIFACTS + aws_secret_access_key: $AWS_SECRET_ACCESS_KEY_ARTIFACTS + region: ${AWS_REGION_ARTIFACTS} - run: # We'll add a bucket lifecycle policy to delete objects tagged as "pull_request" after 30 days. name: Determine S3 object tags based on whether this is a PR @@ -663,13 +672,6 @@ jobs: echo "export S3_OBJECT_TYPE_TAG=base" >> $BASH_ENV fi echo "export S3_OBJECT_BRANCH_TAG=<< pipeline.git.branch >>" >> $BASH_ENV - - aws-cli/setup: - aws_access_key_id: $AWS_ACCESS_KEY_ID_ARTIFACTS - aws_secret_access_key: $AWS_SECRET_ACCESS_KEY_ARTIFACTS - region: ${AWS_REGION_ARTIFACTS} - - run: - name: create a size snapshot - command: pnpm size:snapshot # === LEGACY START === # remove once the UI can handle the new format @@ -679,8 +681,8 @@ jobs: condition: not: matches: - pattern: ".+" - value: "${CIRCLE_PULL_REQUEST}" + pattern: '.+' + value: '${CIRCLE_PULL_REQUEST}' steps: - aws-s3/copy: arguments: --content-type application/json @@ -704,6 +706,7 @@ jobs: --bucket mui-org-ci \ --key artifacts/mui/material-ui/$CIRCLE_SHA1/size-snapshot.json \ --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TYPE_TAG},{Key=branch,Value=$S3_OBJECT_BRANCH_TAG}]" + - run: name: Run danger on PRs command: pnpm danger ci --fail-on-errors From a072dcfb490846ca9dec2374a69b6d72873a972f Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:44:38 +0200 Subject: [PATCH 12/18] mv --- .../bundle-size-checker/bin/bundle-size-checker.js | 2 +- packages-internal/bundle-size-checker/package.json | 2 +- packages-internal/bundle-size-checker/{ => src}/configLoader.js | 0 packages-internal/bundle-size-checker/{ => src}/create.js | 0 packages-internal/bundle-size-checker/{ => src}/defineConfig.js | 0 packages-internal/bundle-size-checker/{ => src}/index.js | 0 .../bundle-size-checker/{ => src}/loadComparison.js | 0 packages-internal/bundle-size-checker/{ => src}/types.d.ts | 0 packages-internal/bundle-size-checker/{ => src}/worker.js | 0 9 files changed, 2 insertions(+), 2 deletions(-) rename packages-internal/bundle-size-checker/{ => src}/configLoader.js (100%) rename packages-internal/bundle-size-checker/{ => src}/create.js (100%) rename packages-internal/bundle-size-checker/{ => src}/defineConfig.js (100%) rename packages-internal/bundle-size-checker/{ => src}/index.js (100%) rename packages-internal/bundle-size-checker/{ => src}/loadComparison.js (100%) rename packages-internal/bundle-size-checker/{ => src}/types.d.ts (100%) rename packages-internal/bundle-size-checker/{ => src}/worker.js (100%) diff --git a/packages-internal/bundle-size-checker/bin/bundle-size-checker.js b/packages-internal/bundle-size-checker/bin/bundle-size-checker.js index 349b524e320693..a1b907fb44aa10 100755 --- a/packages-internal/bundle-size-checker/bin/bundle-size-checker.js +++ b/packages-internal/bundle-size-checker/bin/bundle-size-checker.js @@ -1,3 +1,3 @@ #!/usr/bin/env node -import '../create.js'; +import '../src/create.js'; diff --git a/packages-internal/bundle-size-checker/package.json b/packages-internal/bundle-size-checker/package.json index af667287c9c706..7b73359ced2420 100644 --- a/packages-internal/bundle-size-checker/package.json +++ b/packages-internal/bundle-size-checker/package.json @@ -4,7 +4,7 @@ "private": true, "description": "Bundle size checker for MUI packages", "type": "module", - "main": "./index.js", + "main": "./src/index.js", "bin": { "bundle-size-checker": "./bin/bundle-size-checker.js" }, diff --git a/packages-internal/bundle-size-checker/configLoader.js b/packages-internal/bundle-size-checker/src/configLoader.js similarity index 100% rename from packages-internal/bundle-size-checker/configLoader.js rename to packages-internal/bundle-size-checker/src/configLoader.js diff --git a/packages-internal/bundle-size-checker/create.js b/packages-internal/bundle-size-checker/src/create.js similarity index 100% rename from packages-internal/bundle-size-checker/create.js rename to packages-internal/bundle-size-checker/src/create.js diff --git a/packages-internal/bundle-size-checker/defineConfig.js b/packages-internal/bundle-size-checker/src/defineConfig.js similarity index 100% rename from packages-internal/bundle-size-checker/defineConfig.js rename to packages-internal/bundle-size-checker/src/defineConfig.js diff --git a/packages-internal/bundle-size-checker/index.js b/packages-internal/bundle-size-checker/src/index.js similarity index 100% rename from packages-internal/bundle-size-checker/index.js rename to packages-internal/bundle-size-checker/src/index.js diff --git a/packages-internal/bundle-size-checker/loadComparison.js b/packages-internal/bundle-size-checker/src/loadComparison.js similarity index 100% rename from packages-internal/bundle-size-checker/loadComparison.js rename to packages-internal/bundle-size-checker/src/loadComparison.js diff --git a/packages-internal/bundle-size-checker/types.d.ts b/packages-internal/bundle-size-checker/src/types.d.ts similarity index 100% rename from packages-internal/bundle-size-checker/types.d.ts rename to packages-internal/bundle-size-checker/src/types.d.ts diff --git a/packages-internal/bundle-size-checker/worker.js b/packages-internal/bundle-size-checker/src/worker.js similarity index 100% rename from packages-internal/bundle-size-checker/worker.js rename to packages-internal/bundle-size-checker/src/worker.js From 4596cf0dec06771ebd2184273f857072b51d9e29 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed, 23 Apr 2025 13:13:48 +0200 Subject: [PATCH 13/18] Squashed commit of the following: commit 54328c91a23b17fd1162be7ac79bd557b6ed6aa9 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 13:02:21 2025 +0200 Update renderMarkdownReport.js commit a7a84dd3057aae959fc8521c85778ef72425c16d Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 12:52:14 2025 +0200 Update renderMarkdownReport.js commit d5a4f41ebdc63b64dcccc6534de72d685e8d3e9c Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 12:50:09 2025 +0200 Update renderMarkdownReport.js commit 3add681d6d713e353f837301f50f106b25666aae Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 12:48:56 2025 +0200 Update renderMarkdownReport.js commit 6d59d4de0a50be31408e3fc83e136587f0ab02c3 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 12:33:44 2025 +0200 Update renderMarkdownReport.js commit 2760edcf6ed47380f5dbf3fe584958fa423c2e58 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 11:44:44 2025 +0200 Update index.js commit c0feb510965f2f90492a784ce89ada628aa83968 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 10:55:33 2025 +0200 Update renderMarkdownReport.js commit 33673c4685c2c85c23ead32a6cc7deacdd172847 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 10:53:31 2025 +0200 Update dangerFileContent.ts commit a48e2c5ffade5df528848a7bf67b7e61d1ea7f2b Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 10:52:54 2025 +0200 hgf commit a66d7a5cc18259d4e437c7563d8302ddbc9ef4b3 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 10:36:11 2025 +0200 Update renderMarkdownReport.js commit f86dac1ba6a39a1dc37a7675413df5253294bd62 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 10:35:25 2025 +0200 Update renderMarkdownReport.js commit 802da054dabbdb60121093e603abbcd40ca0129e Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 10:23:04 2025 +0200 Update renderMarkdownReport.js commit 5d34f9eeb0a68a60e2d5d8aa205d3b7e0173fd73 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 10:01:17 2025 +0200 Update fetchSnapshot.js commit 32c6f0e8c1be3b6d33eded573994e45182b4dca9 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 20:35:18 2025 +0200 refactor commit d181d0b41b80099446c09022ea4ccc3b942d62f1 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 18:22:40 2025 +0200 Update dangerFileContent.ts commit 54faf15a11b2a32e78f3c1fd7bc3410fe9d05935 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 18:22:17 2025 +0200 Update dangerFileContent.ts commit fd28d71bd20c4debbff7f05bd8b9ed9af092d333 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 18:21:59 2025 +0200 circleciBuildNumber commit eb65bf06846b17162b7352a23b665e84ff066179 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 18:08:55 2025 +0200 Update dangerFileContent.ts commit 4de9e83d2ceb28758e8f03fc06e08c8078ee45b9 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 18:07:37 2025 +0200 Update renderMarkdownReport.js commit 7d83034b01b6e5d32eab8983cad1c05f60bb0808 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 17:44:25 2025 +0200 frefer commit 3578d19c762925a88dc83bbdd0e0a7a136902b2a Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:52:25 2025 +0200 Update renderMarkdownReport.js commit edf282d1771f9376e7d0a458fa4aeb5eba66d42e Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:41:09 2025 +0200 Update renderMarkdownReport.js commit 7e5637f16f0bc2c71b8f0aa167dbe36d0afa98d8 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:40:31 2025 +0200 Update renderMarkdownReport.js commit a2830e147fa21d32348e616f83f4c708dfd00042 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:39:15 2025 +0200 Update renderMarkdownReport.js commit b3d918c3aeb6db95eb7cc74281d8ac5515f0d012 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:38:35 2025 +0200 add in front for less visual clutter commit 11af7181af9ff814761c634c6c728c24c4899af2 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:38:05 2025 +0200 reduce amount of triangles commit e15e2494326b33d00e841aa67533e42ed71f134e Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:28:49 2025 +0200 Update dangerFileContent.ts commit d67a4b312dbdbdb892cc09820b305270fbd35ccc Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:28:25 2025 +0200 frwefre commit c4fa1c5f2784f1d52808dd48527826b363b8b1be Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:19:43 2025 +0200 Update renderMarkdownReport.js commit ce11999b8df6534c94745bad9c639650b041b3a1 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:17:47 2025 +0200 Update renderMarkdownReport.js commit ea005404290fb9e30e3526b9e73053da59839d51 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:16:39 2025 +0200 Update renderMarkdownReport.js commit 10c680fe868a2297895be6e067e32b14c5801f43 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:16:21 2025 +0200 Update renderMarkdownReport.js commit ff49d9b7385419d102d29fd86533945776d2579d Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:15:49 2025 +0200 remove loadComparison commit 41daac9dfe6618c45ffc85c585b1e7b45375a958 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:15:08 2025 +0200 refactor commit 3ae5757504d1841369f8a0c0a31e8f120c66eb17 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 16:01:43 2025 +0200 fix url commit a6d5b75f41a088171143110ceb8bdb13ccbde678 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue Apr 22 15:48:56 2025 +0200 WIP --- dangerFileContent.ts | 178 +------------- .../bundle-size-checker/src/fetchSnapshot.js | 21 ++ .../bundle-size-checker/src/index.js | 6 +- .../bundle-size-checker/src/loadComparison.js | 86 ------- .../src/renderMarkdownReport.js | 232 ++++++++++++++++++ .../bundle-size-checker/src/sizeDiff.js | 199 +++++++++++++++ 6 files changed, 467 insertions(+), 255 deletions(-) create mode 100644 packages-internal/bundle-size-checker/src/fetchSnapshot.js delete mode 100644 packages-internal/bundle-size-checker/src/loadComparison.js create mode 100644 packages-internal/bundle-size-checker/src/renderMarkdownReport.js create mode 100644 packages-internal/bundle-size-checker/src/sizeDiff.js diff --git a/dangerFileContent.ts b/dangerFileContent.ts index bfc8d7b8c11bb1..61ce02ffd608dd 100644 --- a/dangerFileContent.ts +++ b/dangerFileContent.ts @@ -1,7 +1,6 @@ -import { exec } from 'child_process'; import type * as dangerModule from 'danger'; import replaceUrl from '@mui-internal/api-docs-builder/utils/replaceUrl'; -import { loadComparison } from '@mui/internal-bundle-size-checker'; +import { renderMarkdownReport } from '@mui/internal-bundle-size-checker'; declare const danger: (typeof dangerModule)['danger']; declare const markdown: (typeof dangerModule)['markdown']; @@ -10,98 +9,6 @@ const circleCIBuildNumber = process.env.CIRCLE_BUILD_NUM; const circleCIBuildUrl = `https://app.circleci.com/pipelines/github/mui/material-ui/jobs/${circleCIBuildNumber}`; const dangerCommand = process.env.DANGER_COMMAND; -const parsedSizeChangeThreshold = 300; -const gzipSizeChangeThreshold = 100; - -/** - * Executes a git subcommand. - * @param {any} args - */ -function git(args: any) { - return new Promise((resolve, reject) => { - exec(`git ${args}`, (err, stdout) => { - if (err) { - reject(err); - } else { - resolve(stdout.trim()); - } - }); - }); -} - -const UPSTREAM_REMOTE = 'danger-upstream'; - -/** - * This is mainly used for local development. It should be executed before the - * scripts exit to avoid adding internal remotes to the local machine. This is - * not an issue in CI. - */ -async function reportBundleSizeCleanup() { - await git(`remote remove ${UPSTREAM_REMOTE}`); -} - -/** - * Creates a callback for Object.entries(comparison).filter that excludes every - * entry that does not exceed the given threshold values for parsed and gzip size. - * @param {number} parsedThreshold - * @param {number} gzipThreshold - */ -function createComparisonFilter(parsedThreshold: number, gzipThreshold: number) { - return (comparisonEntry: any) => { - const [, snapshot] = comparisonEntry; - return ( - Math.abs(snapshot.parsed.absoluteDiff) >= parsedThreshold || - Math.abs(snapshot.gzip.absoluteDiff) >= gzipThreshold - ); - }; -} - -/** - * Generates a user-readable string from a percentage change. - * @param {number} change - * @param {string} goodEmoji emoji on reduction - * @param {string} badEmoji emoji on increase - */ -function addPercent(change: number, goodEmoji = '', badEmoji = ':small_red_triangle:') { - const formatted = (change * 100).toFixed(2); - if (/^-|^0(?:\.0+)$/.test(formatted)) { - return `${formatted}% ${goodEmoji}`; - } - return `+${formatted}% ${badEmoji}`; -} - -function generateEmphasizedChange([bundle, { parsed, gzip }]: [ - string, - { parsed: { relativeDiff: number }; gzip: { relativeDiff: number } }, -]) { - // increase might be a bug fix which is a nice thing. reductions are always nice - const changeParsed = addPercent(parsed.relativeDiff, ':heart_eyes:', ''); - const changeGzip = addPercent(gzip.relativeDiff, ':heart_eyes:', ''); - - return `**${bundle}**: parsed: ${changeParsed}, gzip: ${changeGzip}`; -} - -/** - * Puts results in different buckets. - * @param {*} results - */ -function sieveResults(results: Array<[string, T]>) { - const main: [string, T][] = []; - const pages: [string, T][] = []; - - results.forEach((entry) => { - const [bundleId] = entry; - - if (bundleId.startsWith('docs:')) { - pages.push(entry); - } else { - main.push(entry); - } - }); - - return { all: results, main, pages }; -} - function prepareBundleSizeReport() { markdown( `## Bundle size report @@ -110,80 +17,21 @@ Bundle size will be reported once [CircleCI build #${circleCIBuildNumber}](${cir ); } -// A previous build might have failed to produce a snapshot -// Let's walk up the tree a bit until we find a commit that has a successful snapshot -async function loadLastComparison( - upstreamRef: any, - n = 0, -): Promise>> { - const mergeBaseCommit = await git(`merge-base HEAD~${n} ${UPSTREAM_REMOTE}/${upstreamRef}`); - try { - return await loadComparison(mergeBaseCommit, upstreamRef); - } catch (err) { - if (n >= 5) { - throw err; - } - return loadLastComparison(upstreamRef, n + 1); - } -} +// These functions are no longer needed as they've been moved to the prSizeDiff.js module async function reportBundleSize() { - // Use git locally to grab the commit which represents the place where the branches differ - const upstreamRepo = danger.github.pr.base.repo.full_name; - const upstreamRef = danger.github.pr.base.ref; - try { - await git(`remote add ${UPSTREAM_REMOTE} https://github.com/${upstreamRepo}.git`); - } catch (err) { - // ignore if it already exist for local testing - } - await git(`fetch ${UPSTREAM_REMOTE}`); - - const comparison = await loadLastComparison(upstreamRef); - - const detailedComparisonQuery = `circleCIBuildNumber=${circleCIBuildNumber}&baseRef=${danger.github.pr.base.ref}&baseCommit=${comparison.previous}&prNumber=${danger.github.pr.number}`; - const detailedComparisonToolpadUrl = `https://tools-public.mui.com/prod/pages/bundleSizes?${detailedComparisonQuery}`; - const detailedComparisonRoute = `/size-comparison?${detailedComparisonQuery}`; - const detailedComparisonUrl = `https://mui-dashboard.netlify.app${detailedComparisonRoute}`; + let markdownContent = `## Bundle size report\n\n`; - const { all: allResults, main: mainResults } = sieveResults(Object.entries(comparison.bundles)); - const anyResultsChanges = allResults.filter(createComparisonFilter(1, 1)); - - if (anyResultsChanges.length > 0) { - const importantChanges = mainResults - .filter(createComparisonFilter(parsedSizeChangeThreshold, gzipSizeChangeThreshold)) - .sort(([, a], [, b]) => { - const aDiff = Math.abs(a.parsed.absoluteDiff) + Math.abs(a.gzip.absoluteDiff); - const bDiff = Math.abs(b.parsed.absoluteDiff) + Math.abs(b.gzip.absoluteDiff); - return bDiff - aDiff; - }) - .map(generateEmphasizedChange); - - // have to guard against empty strings - if (importantChanges.length > 0) { - const maxVisible = 20; - - const lines = importantChanges.slice(0, maxVisible); - - const nrOfHiddenChanges = Math.max(0, importantChanges.length - maxVisible); - if (nrOfHiddenChanges > 0) { - lines.push(`and [${nrOfHiddenChanges} more changes](${detailedComparisonToolpadUrl})`); - } - - markdown(lines.join('\n')); - } - - const details = `## Bundle size report + if (!process.env.CIRCLE_BUILD_NUM) { + throw new Error('CIRCLE_BUILD_NUM is not defined'); + } -[Details of bundle changes (Toolpad)](${detailedComparisonToolpadUrl}) -[Details of bundle changes](${detailedComparisonUrl})`; + const circleciBuildNumber = process.env.CIRCLE_BUILD_NUM; - markdown(details); - } else { - markdown(`## Bundle size report + markdownContent += await renderMarkdownReport(danger.github.pr, circleciBuildNumber); -[No bundle size changes (Toolpad)](${detailedComparisonToolpadUrl}) -[No bundle size changes](${detailedComparisonUrl})`); - } + // Use the markdown function to publish the report + markdown(markdownContent); } function addDeployPreviewUrls() { @@ -247,11 +95,7 @@ async function run() { prepareBundleSizeReport(); break; case 'reportBundleSize': - try { - await reportBundleSize(); - } finally { - await reportBundleSizeCleanup(); - } + await reportBundleSize(); break; default: throw new TypeError(`Unrecognized danger command '${dangerCommand}'`); diff --git a/packages-internal/bundle-size-checker/src/fetchSnapshot.js b/packages-internal/bundle-size-checker/src/fetchSnapshot.js new file mode 100644 index 00000000000000..6442df49327f53 --- /dev/null +++ b/packages-internal/bundle-size-checker/src/fetchSnapshot.js @@ -0,0 +1,21 @@ +/** + * + * @param {string} repo - The name of the repository e.g. 'mui/material-ui' + * @param {string} sha - The commit SHA + * @returns {Promise} - The size snapshot data + */ +export async function fetchSnapshot(repo, sha) { + const url = `https://s3.eu-central-1.amazonaws.com/mui-org-ci/artifacts/${repo}/${sha}/size-snapshot.json`; + const response = await fetch(url); + if (!response.ok) { + if (repo === 'mui/material-ui') { + const legacyUrl = `https://s3.eu-central-1.amazonaws.com/mui-org-ci/artifacts/master/${sha}/size-snapshot.json`; + const legacyResponse = await fetch(legacyUrl); + if (legacyResponse.ok) { + return legacyResponse.json(); + } + } + throw new Error(`Failed to fetch "${url}", HTTP ${response.status}`); + } + return response.json(); +} diff --git a/packages-internal/bundle-size-checker/src/index.js b/packages-internal/bundle-size-checker/src/index.js index baaf1c394e5450..94d7e5184e5f95 100644 --- a/packages-internal/bundle-size-checker/src/index.js +++ b/packages-internal/bundle-size-checker/src/index.js @@ -1,5 +1,7 @@ -import loadComparison from './loadComparison.js'; import defineConfig from './defineConfig.js'; import { loadConfig } from './configLoader.js'; +import { calculateSizeDiff } from './sizeDiff.js'; +import { renderMarkdownReport } from './renderMarkdownReport.js'; +import { fetchSnapshot } from './fetchSnapshot.js'; -export { loadComparison, defineConfig, loadConfig }; +export { defineConfig, loadConfig, calculateSizeDiff, renderMarkdownReport, fetchSnapshot }; diff --git a/packages-internal/bundle-size-checker/src/loadComparison.js b/packages-internal/bundle-size-checker/src/loadComparison.js deleted file mode 100644 index f64b4a182f3b53..00000000000000 --- a/packages-internal/bundle-size-checker/src/loadComparison.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * `snapshots` always refer to size snapshots in this file - */ -import path from 'path'; -import fse from 'fs-extra'; - -const ARTIFACT_SERVER = 'https://s3.eu-central-1.amazonaws.com/mui-org-ci'; - -/** - * @param {string} [snapshotPath] - Optional path to the snapshot file - */ -async function loadCurrentSnapshot(snapshotPath) { - const filePath = snapshotPath || path.join(process.cwd(), 'size-snapshot.json'); - return fse.readJSON(filePath); -} - -/** - * @param {string} commitId - the sha of a commit - * @param {string} ref - the branch containing that commit - */ -async function loadSnapshot(commitId, ref) { - if (ref === undefined) { - throw new TypeError( - `Need a ref for that commit. Did you mean \`loadSnapshot(commitId, 'master')\`?`, - ); - } - const url = `${ARTIFACT_SERVER}/artifacts/${ref}/${commitId}/size-snapshot.json`; - const response = await fetch(url); - if (!response.ok) { - throw new Error(`Failed to fetch "${url}", HTTP ${response.status}`); - } - return response.json(); -} - -const nullSnapshot = { parsed: 0, gzip: 0 }; - -/** - * @param {string} parentId - The commit ID - * @param {string} ref - The branch reference - * @param {string} [snapshotPath] - Optional path to the current snapshot file - */ -export default async function loadComparison(parentId, ref, snapshotPath) { - const [currentSnapshot, previousSnapshot] = await Promise.all([ - loadCurrentSnapshot(snapshotPath), - // continue non existing snapshots - loadSnapshot(parentId, ref).catch((reason) => { - console.warn(`Failed to load snapshot for ref '${ref}' and commit '${parentId}': `, reason); - return {}; - }), - ]); - - const bundleKeys = Object.keys({ ...currentSnapshot, ...previousSnapshot }); - - const bundles = Object.fromEntries( - bundleKeys.map((bundle) => { - // if a bundle was added the change should be +inf - // if a bundle was removed the change should be -100% - const currentSize = currentSnapshot[bundle] || nullSnapshot; - const previousSize = previousSnapshot[bundle] || nullSnapshot; - - return [ - bundle, - { - parsed: { - previous: previousSize.parsed, - current: currentSize.parsed, - absoluteDiff: currentSize.parsed - previousSize.parsed, - relativeDiff: currentSize.parsed / previousSize.parsed - 1, - }, - gzip: { - previous: previousSize.gzip, - current: currentSize.gzip, - absoluteDiff: currentSize.gzip - previousSize.gzip, - relativeDiff: currentSize.gzip / previousSize.gzip - 1, - }, - }, - ]; - }), - ); - - return { - previous: parentId, - current: 'HEAD', - bundles, - }; -} diff --git a/packages-internal/bundle-size-checker/src/renderMarkdownReport.js b/packages-internal/bundle-size-checker/src/renderMarkdownReport.js new file mode 100644 index 00000000000000..03d6b436341f86 --- /dev/null +++ b/packages-internal/bundle-size-checker/src/renderMarkdownReport.js @@ -0,0 +1,232 @@ +/** + * @typedef {import('./sizeDiff.js').Size} Size + * @typedef {import('./sizeDiff.js').SizeSnapshot} SizeSnapshot + * @typedef {import('./sizeDiff.js').ComparisonResult} ComparisonResult + */ + +import { calculateSizeDiff } from './sizeDiff.js'; +import { fetchSnapshot } from './fetchSnapshot.js'; + +const displayPercentFormatter = new Intl.NumberFormat(undefined, { + style: 'percent', + signDisplay: 'exceptZero', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + useGrouping: true, +}); + +// Formatter for byte sizes (absolute values) - no sign +const byteSizeFormatter = new Intl.NumberFormat(undefined, { + style: 'unit', + unit: 'byte', + notation: 'compact', + unitDisplay: 'narrow', + maximumSignificantDigits: 3, + minimumSignificantDigits: 1, +}); + +// Formatter for size changes - always show sign +// Created by extending the options from byteSizeFormatter +const byteSizeChangeFormatter = new Intl.NumberFormat(undefined, { + ...byteSizeFormatter.resolvedOptions(), + signDisplay: 'exceptZero', +}); + +/** + * + * @param {'▲' | '▼'} symbol + * @param {'yellow'|'red'|'blue'|'green'} color + * @returns + */ +function formatSymbol(symbol, color) { + return `\${\\tiny{\\color{${color}}${symbol}}}$`; +} + +/** + * Generates a symbol based on the relative change value. + * @param {number|null} relative - The relative change as a Number + * @returns {string} Formatted size change string with symbol + */ +function getChangeIcon(relative) { + if (relative === null) { + return formatSymbol('▲', 'yellow'); + } + if (relative === -1) { + return formatSymbol('▼', 'blue'); + } + if (relative < 0) { + return formatSymbol('▼', 'green'); + } + if (relative > 0) { + return formatSymbol('▲', 'red'); + } + return ' '; +} + +/** + * Formats the relative change value for display. + * @param {number|null} value - The relative change as a Number + * @returns {string} Formatted relative change string + */ +function formatRelativeChange(value) { + if (value === null) { + return 'new'; + } + if (value === -1) { + return 'removed'; + } + return displayPercentFormatter.format(value); +} + +/** + * Generates a user-readable string from a percentage change. + * @param {number} absolute - The absolute change as a Number + * @param {number|null} relative - The relative change as a Number + * @returns {string} Formatted percentage string with emoji + */ +function formatChange(absolute, relative) { + const formattedAbsolute = byteSizeChangeFormatter.format(absolute); + const formattedChange = formatRelativeChange(relative); + return `${getChangeIcon(relative)}${formattedAbsolute}(${formattedChange})`; +} + +/** + * Generates emphasized change text for a single bundle + * @param {Size} entry - Bundle entry + * @returns {string} Formatted change text + */ +function generateEmphasizedChange({ id: bundle, parsed, gzip }) { + // increase might be a bug fix which is a nice thing. reductions are always nice + const changeParsed = formatChange(parsed.absoluteDiff, parsed.relativeDiff); + const changeGzip = formatChange(gzip.absoluteDiff, gzip.relativeDiff); + + return `**${bundle}** **parsed:**${changeParsed} **gzip:**${changeGzip}`; +} + +/** + * Generates a Markdown report for bundle size changes + * @param {ComparisonResult} comparison - Comparison result from calculateSizeDiff + * @param {Object} [options] - Additional options + * @param {number} [options.visibleLimit=10] - Number of entries to show before collapsing + * @param {number} [options.parsedSizeChangeThreshold=300] - Threshold for parsed size change by which to show the entry + * @param {number} [options.gzipSizeChangeThreshold=100] - Threshold for gzipped size change by which to show the entry + * @returns {string} Markdown report + */ +function renderMarkdownReportContent( + comparison, + { visibleLimit = 10, parsedSizeChangeThreshold = 300, gzipSizeChangeThreshold = 100 } = {}, +) { + let markdownContent = ''; + + markdownContent += `**Total Size Change:**${formatChange( + comparison.totals.totalParsed, + comparison.totals.totalParsedPercent, + )} - **Total Gzip Change:**${formatChange( + comparison.totals.totalGzip, + comparison.totals.totalGzipPercent, + )}\n`; + + markdownContent += `Files: ${comparison.fileCounts.total} total (${ + comparison.fileCounts.added + } added, ${comparison.fileCounts.removed} removed, ${comparison.fileCounts.changed} changed)\n\n`; + + const changedEntries = comparison.entries.filter( + (entry) => Math.abs(entry.parsed.absoluteDiff) > 0 || Math.abs(entry.gzip.absoluteDiff) > 0, + ); + + const visibleEntries = []; + const hiddenEntries = []; + + for (const entry of changedEntries) { + const { parsed, gzip } = entry; + const isSignificantChange = + Math.abs(parsed.absoluteDiff) > parsedSizeChangeThreshold || + Math.abs(gzip.absoluteDiff) > gzipSizeChangeThreshold; + if (isSignificantChange && visibleEntries.length < visibleLimit) { + visibleEntries.push(entry); + } else { + hiddenEntries.push(entry); + } + } + + const importantChanges = visibleEntries.map(generateEmphasizedChange); + const hiddenChanges = hiddenEntries.map(generateEmphasizedChange); + + // Add important changes to markdown + if (importantChanges.length > 0) { + // Show the most significant changes first, up to the visible limit + const visibleChanges = importantChanges.slice(0, visibleLimit); + markdownContent += `${visibleChanges.join('\n')}`; + } + + // If there are more changes, add them in a collapsible details section + if (hiddenChanges.length > 0) { + markdownContent += `\n
\nShow ${hiddenChanges.length} more bundle changes\n\n`; + markdownContent += `${hiddenChanges.join('\n')}\n\n`; + markdownContent += `
`; + } + + return markdownContent; +} + +/** + * + * @param {Object} prInfo + * @param {number} prInfo.number - The pull request number + * @param {Object} prInfo.base - The base branch of the pull request + * @param {string} prInfo.base.ref - The base branch name + * @param {string} prInfo.base.sha - The base branch SHA + * @param {string} [circleciBuildNumber] - The CircleCI build number + * @returns {URL} + */ +function getDetailsUrl(prInfo, circleciBuildNumber) { + const detailedComparisonUrl = new URL('https://mui-dashboard.netlify.app/size-comparison'); + detailedComparisonUrl.searchParams.set('baseRef', prInfo.base.ref); + detailedComparisonUrl.searchParams.set('baseCommit', prInfo.base.sha); + detailedComparisonUrl.searchParams.set('prNumber', String(prInfo.number)); + if (circleciBuildNumber) { + detailedComparisonUrl.searchParams.set('circleCIBuildNumber', circleciBuildNumber); + } + return detailedComparisonUrl; +} + +/** + * + * @param {Object} prInfo + * @param {number} prInfo.number - The pull request number + * @param {Object} prInfo.base - The base branch of the pull request + * @param {string} prInfo.base.ref - The base branch name + * @param {string} prInfo.base.sha - The base branch SHA + * @param {Object} prInfo.head - The head branch of the pull request + * @param {string} prInfo.head.ref - The head branch name + * @param {string} prInfo.head.sha - The head branch SHA + * @param {string} [circleciBuildNumber] - The CircleCI build number + * @returns + */ +export async function renderMarkdownReport(prInfo, circleciBuildNumber) { + let markdownContent = ''; + + const baseCommit = prInfo.base.sha; + const prCommit = prInfo.head.sha; + const [baseSnapshot, prSnapshot] = await Promise.all([ + fetchSnapshot('mui/material-ui', baseCommit).catch((error) => { + console.error(`Error fetching base snapshot: ${error}`); + return null; + }), + fetchSnapshot('mui/material-ui', prCommit), + ]); + + if (!baseSnapshot) { + markdownContent += `_:no_entry_sign: No bundle size snapshot found for base commit ${baseCommit}._\n\n`; + } + + const sizeDiff = calculateSizeDiff(baseSnapshot ?? {}, prSnapshot); + + const report = await renderMarkdownReportContent(sizeDiff); + + markdownContent += report; + + markdownContent += `\n\n[Details of bundle changes](${getDetailsUrl(prInfo, circleciBuildNumber)})`; + + return markdownContent; +} diff --git a/packages-internal/bundle-size-checker/src/sizeDiff.js b/packages-internal/bundle-size-checker/src/sizeDiff.js new file mode 100644 index 00000000000000..2fa81481d5cd34 --- /dev/null +++ b/packages-internal/bundle-size-checker/src/sizeDiff.js @@ -0,0 +1,199 @@ +/** + * @description Represents a single bundle size entry + * @typedef {Object} SizeSnapshotEntry + * @property {number} parsed + * @property {number} gzip + * + * @description Represents a single bundle size snapshot + * @typedef {Object.} SizeSnapshot + * + * @description Represents a single bundle size comparison + * @typedef {Object} Size + * @property {string} id - Bundle identifier + * @property {Object} parsed - Parsed size information + * @property {number} parsed.previous - Previous parsed size + * @property {number} parsed.current - Current parsed size + * @property {number} parsed.absoluteDiff - Absolute difference in parsed size + * @property {number|null} parsed.relativeDiff - Relative difference in parsed size + * @property {Object} gzip - Gzipped size information + * @property {number} gzip.previous - Previous gzipped size + * @property {number} gzip.current - Current gzipped size + * @property {number} gzip.absoluteDiff - Absolute difference in gzipped size + * @property {number|null} gzip.relativeDiff - Relative difference in gzipped size + * + * @description Represents the comparison results + * @typedef {Object} ComparisonResult + * @property {Size[]} entries - Size entries for each bundle + * @property {Object} totals - Total size information + * @property {number} totals.totalParsed - Total parsed size difference + * @property {number} totals.totalGzip - Total gzipped size difference + * @property {number} totals.totalParsedPercent - Total parsed size percentage difference + * @property {number} totals.totalGzipPercent - Total gzipped size percentage difference + * @property {Object} fileCounts - File count information + * @property {number} fileCounts.added - Number of added files + * @property {number} fileCounts.removed - Number of removed files + * @property {number} fileCounts.changed - Number of changed files + * @property {number} fileCounts.total - Total number of files + */ + +const nullSnapshot = { parsed: 0, gzip: 0 }; + +/** + * Calculates size difference between two snapshots + * + * @param {SizeSnapshot} baseSnapshot - Base snapshot (previous) + * @param {SizeSnapshot} targetSnapshot - Target snapshot (current) + * @returns {ComparisonResult} Comparison result with entries, totals, and file counts + */ +export function calculateSizeDiff(baseSnapshot, targetSnapshot) { + const bundleKeys = Object.keys({ ...baseSnapshot, ...targetSnapshot }); + /** @type {Size[]} */ + const results = []; + + // Track totals + let totalParsed = 0; + let totalGzip = 0; + let totalParsedPrevious = 0; + let totalGzipPrevious = 0; + + // Track file counts + let addedFiles = 0; + let removedFiles = 0; + let changedFiles = 0; + + bundleKeys.forEach((bundle) => { + const isNewBundle = !baseSnapshot[bundle]; + const isRemovedBundle = !targetSnapshot[bundle]; + const currentSize = targetSnapshot[bundle] || nullSnapshot; + const previousSize = baseSnapshot[bundle] || nullSnapshot; + + // Update file counts + if (isNewBundle) { + addedFiles += 1; + } else if (isRemovedBundle) { + removedFiles += 1; + } else if ( + currentSize.parsed !== previousSize.parsed || + currentSize.gzip !== previousSize.gzip + ) { + changedFiles += 1; + } + + const parsedDiff = currentSize.parsed - previousSize.parsed; + const gzipDiff = currentSize.gzip - previousSize.gzip; + + // Calculate relative diffs with appropriate handling of new/removed bundles + let parsedRelativeDiff; + if (isNewBundle) { + parsedRelativeDiff = null; + } else if (isRemovedBundle) { + parsedRelativeDiff = -1; + } else if (previousSize.parsed) { + parsedRelativeDiff = currentSize.parsed / previousSize.parsed - 1; + } else { + parsedRelativeDiff = 0; + } + + let gzipRelativeDiff; + if (isNewBundle) { + gzipRelativeDiff = null; + } else if (isRemovedBundle) { + gzipRelativeDiff = -1; + } else if (previousSize.gzip) { + gzipRelativeDiff = currentSize.gzip / previousSize.gzip - 1; + } else { + gzipRelativeDiff = 0; + } + + const entry = { + id: bundle, + parsed: { + previous: previousSize.parsed, + current: currentSize.parsed, + absoluteDiff: parsedDiff, + relativeDiff: parsedRelativeDiff, + }, + gzip: { + previous: previousSize.gzip, + current: currentSize.gzip, + absoluteDiff: gzipDiff, + relativeDiff: gzipRelativeDiff, + }, + }; + + results.push(entry); + + // Update totals + totalParsed += parsedDiff; + totalGzip += gzipDiff; + totalParsedPrevious += previousSize.parsed; + totalGzipPrevious += previousSize.gzip; + }); + + // Calculate percentage changes + const totalParsedPercent = totalParsedPrevious > 0 ? totalParsed / totalParsedPrevious : 0; + const totalGzipPercent = totalGzipPrevious > 0 ? totalGzip / totalGzipPrevious : 0; + + // Sort the results + // Custom sorting: + // 1. Existing bundles that increased in size (larger increases first) + // 2. New bundles (larger sizes first) + // 3. Existing bundles that decreased in size (larger decreases first) + // 4. Removed bundles (larger sizes first) + // 5. Unchanged bundles (alphabetically) + results.sort((entryA, entryB) => { + // Helper function to determine bundle category (for sorting) + /** @type {(entry: Size) => number} */ + const getCategory = (entry) => { + if (entry.parsed.relativeDiff === null) { + return 2; // New bundle + } + if (entry.parsed.relativeDiff === -1) { + return 4; // Removed bundle + } + if (entry.parsed.relativeDiff > 0) { + return 1; // Increased + } + if (entry.parsed.relativeDiff < 0) { + return 3; // Decreased + } + return 5; // Unchanged + }; + + // Get categories for both bundles + const categoryA = getCategory(entryA); + const categoryB = getCategory(entryB); + + // Sort by category first + if (categoryA !== categoryB) { + return categoryA - categoryB; + } + + // Within the same category, sort by absolute diff (largest first) + const diffA = Math.abs(entryA.parsed.absoluteDiff); + const diffB = Math.abs(entryB.parsed.absoluteDiff); + + if (diffA !== diffB) { + return diffB - diffA; + } + + // If diffs are the same, sort by name + return entryA.id.localeCompare(entryB.id); + }); + + return { + entries: results, + totals: { + totalParsed, + totalGzip, + totalParsedPercent, + totalGzipPercent, + }, + fileCounts: { + added: addedFiles, + removed: removedFiles, + changed: changedFiles, + total: bundleKeys.length, + }, + }; +} From 1ee1a59ef0fea4683a7742a6abb58bbf7b1dd912 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:19:28 +0200 Subject: [PATCH 14/18] Squashed commit of the following: commit 0fd667521fee41e2703f61eded5dfa58603c4314 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 16:11:36 2025 +0200 Update config.yml commit 760ceed7d497da9805e39fbf49704ac761072fe8 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 15:50:18 2025 +0200 @aws-sdk/credential-providers commit 2a5fe14f7b2b3abc018e39ee66cca320d74fe76b Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 15:39:24 2025 +0200 Update README.md commit dc042124f32ae5de019b2573d3eb442852df9908 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 15:30:25 2025 +0200 fixes commit c9f5fb3ba77f2800ab0e31036437d96e60911cd0 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 15:03:31 2025 +0200 Update pnpm-lock.yaml commit 6a4d0f52391de971e579022c5d7a3abc4393b5c2 Author: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed Apr 23 14:49:32 2025 +0200 upload functionality --- .circleci/config.yml | 35 +- bundle-size/bundle-size-checker.config.mjs | 11 + .../bundle-size-checker/README.md | 82 ++ .../bundle-size-checker/package.json | 3 + .../bundle-size-checker/src/configLoader.js | 4 +- .../bundle-size-checker/src/create.js | 25 +- .../bundle-size-checker/src/types.d.ts | 13 + .../bundle-size-checker/src/uploadSnapshot.js | 89 ++ pnpm-lock.yaml | 1286 +++++++++++++++++ 9 files changed, 1516 insertions(+), 32 deletions(-) create mode 100644 packages-internal/bundle-size-checker/README.md create mode 100644 packages-internal/bundle-size-checker/src/uploadSnapshot.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 83fdcb3211899d..5500117e4253ef 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -654,24 +654,18 @@ jobs: - run: name: build @mui packages command: pnpm lerna run --ignore @mui/icons-material --concurrency 6 --scope "@mui/*" build - - run: - name: create a size snapshot - command: pnpm size:snapshot - - aws-cli/setup: aws_access_key_id: $AWS_ACCESS_KEY_ID_ARTIFACTS aws_secret_access_key: $AWS_SECRET_ACCESS_KEY_ARTIFACTS region: ${AWS_REGION_ARTIFACTS} + - run: - # We'll add a bucket lifecycle policy to delete objects tagged as "pull_request" after 30 days. - name: Determine S3 object tags based on whether this is a PR + name: create and upload a size snapshot command: | - if [[ -n "${CIRCLE_PULL_REQUEST}" ]]; then - echo "export S3_OBJECT_TYPE_TAG=pull_request" >> $BASH_ENV - else - echo "export S3_OBJECT_TYPE_TAG=base" >> $BASH_ENV - fi - echo "export S3_OBJECT_BRANCH_TAG=<< pipeline.git.branch >>" >> $BASH_ENV + export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID_ARTIFACTS + export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY_ARTIFACTS + export AWS_REGION=$AWS_REGION_ARTIFACTS + pnpm size:snapshot # === LEGACY START === # remove once the UI can handle the new format @@ -688,24 +682,13 @@ jobs: arguments: --content-type application/json from: size-snapshot.json to: s3://mui-org-ci/artifacts/$CIRCLE_BRANCH/$CIRCLE_SHA1/ + # === LEGACY END === + + # Keep the artifact storage as a CircleCI artifact - store_artifacts: name: persist size snapshot as pipeline artifact path: size-snapshot.json destination: size-snapshot.json - # === LEGACY END === - - # persist size snapshot on S3 (new format = ///size-snapshot.json) - - aws-s3/copy: - arguments: --content-type application/json - from: size-snapshot.json - to: s3://mui-org-ci/artifacts/mui/material-ui/$CIRCLE_SHA1/ - - run: - name: Tag size-snapshot.json - command: | - aws s3api put-object-tagging \ - --bucket mui-org-ci \ - --key artifacts/mui/material-ui/$CIRCLE_SHA1/size-snapshot.json \ - --tagging "TagSet=[{Key=type,Value=$S3_OBJECT_TYPE_TAG},{Key=branch,Value=$S3_OBJECT_BRANCH_TAG}]" - run: name: Run danger on PRs diff --git a/bundle-size/bundle-size-checker.config.mjs b/bundle-size/bundle-size-checker.config.mjs index a610bbf8ef9ea7..9d24bfaf173d10 100644 --- a/bundle-size/bundle-size-checker.config.mjs +++ b/bundle-size/bundle-size-checker.config.mjs @@ -29,6 +29,9 @@ export default defineConfig(async () => { return `@mui/lab/${componentName}`; }); + // Determine if we're in a CI environment and if it's a PR + const isPullRequest = process.env.CIRCLE_PULL_REQUEST ? true : false; + // Return the complete entrypoints configuration return { entrypoints: [ @@ -47,5 +50,13 @@ export default defineConfig(async () => { '@mui/material/useScrollTrigger', '@mui/utils', ], + // Only add upload config when in CI environment + ...(process.env.CI && { + upload: { + project: 'mui/material-ui', + branch: process.env.CIRCLE_BRANCH, + isPullRequest, + }, + }), }; }); diff --git a/packages-internal/bundle-size-checker/README.md b/packages-internal/bundle-size-checker/README.md new file mode 100644 index 00000000000000..1197cf4bb656ad --- /dev/null +++ b/packages-internal/bundle-size-checker/README.md @@ -0,0 +1,82 @@ +# Bundle Size Checker + +A tool to check and track the bundle size of MUI packages. + +## Features + +- Measures minified and gzipped bundle sizes of packages and components +- Compares bundle sizes between versions +- Generates markdown reports +- Uploads snapshots to S3 for persistent storage and comparison + +## Usage + +### CLI + +```bash +bundle-size-checker [options] +``` + +Options: + +- `--analyze`: Creates a webpack-bundle-analyzer report for each bundle +- `--accurateBundles`: Displays used bundles accurately at the cost of more CPU cycles +- `--output`, `-o`: Path to output the size snapshot JSON file + +### Configuration + +Create a `bundle-size-checker.config.js` or `bundle-size-checker.config.mjs` file: + +```js +import { defineConfig } from '@mui/internal-bundle-size-checker'; + +export default defineConfig(async () => { + return { + entrypoints: [ + // List of packages and components to measure + '@mui/material', + '@mui/material/Button', + // ... + ], + // Optional upload configuration + upload: { + project: 'organization/repository', + branch: 'main', // Optional, defaults to current git branch + isPullRequest: false, // Optional, defaults to false + }, + }; +}); +``` + +### S3 Upload + +When the `upload` configuration is provided, the snapshot will be uploaded to S3 after generation. + +The snapshot will be uploaded to: + +```bash +s3://mui-org-ci/artifacts/{project}/{commit-sha}/size-snapshot.json +``` + +The following tags will be applied: + +- `isPullRequest`: 'yes' or 'no' +- `branch`: The branch name + +Required AWS environment variables: + +- `AWS_ACCESS_KEY_ID` or `AWS_ACCESS_KEY_ID_ARTIFACTS` +- `AWS_SECRET_ACCESS_KEY` or `AWS_SECRET_ACCESS_KEY_ARTIFACTS` +- `AWS_REGION` or `AWS_REGION_ARTIFACTS` (defaults to 'eu-central-1') + +If the upload fails, the CLI will exit with an error code. + +## API + +The library exports the following functions: + +- `defineConfig`: Helper for defining configuration with TypeScript support +- `loadConfig`: Loads configuration from file +- `calculateSizeDiff`: Calculates size differences between snapshots +- `renderMarkdownReport`: Generates markdown reports from size comparisons +- `fetchSnapshot`: Fetches size snapshots from S3 diff --git a/packages-internal/bundle-size-checker/package.json b/packages-internal/bundle-size-checker/package.json index 7b73359ced2420..10b9cae5815ffc 100644 --- a/packages-internal/bundle-size-checker/package.json +++ b/packages-internal/bundle-size-checker/package.json @@ -12,7 +12,10 @@ "typescript": "tsc -p tsconfig.json" }, "dependencies": { + "@aws-sdk/client-s3": "^3.515.0", + "@aws-sdk/credential-providers": "^3.787.0", "compression-webpack-plugin": "^10.0.0", + "execa": "^7.2.0", "fast-glob": "^3.3.2", "fs-extra": "^11.2.0", "piscina": "^4.2.1", diff --git a/packages-internal/bundle-size-checker/src/configLoader.js b/packages-internal/bundle-size-checker/src/configLoader.js index b533a3e8449819..c126a0a5f45b8a 100644 --- a/packages-internal/bundle-size-checker/src/configLoader.js +++ b/packages-internal/bundle-size-checker/src/configLoader.js @@ -8,7 +8,7 @@ import path from 'path'; /** * Attempts to load and parse a single config file * @param {string} configPath - Path to the configuration file - * @returns {Promise<{entrypoints: string[]} | null>} The parsed config or null if file doesn't exist + * @returns {Promise} The parsed config or null if file doesn't exist * @throws {Error} If the file exists but has invalid format */ async function loadConfigFile(configPath) { @@ -43,7 +43,7 @@ async function loadConfigFile(configPath) { /** * Attempts to load the config file from the given directory * @param {string} rootDir - The directory to search for the config file - * @returns {Promise<{entrypoints: string[]}>} A promise that resolves to the config object + * @returns {Promise} A promise that resolves to the config object */ export async function loadConfig(rootDir) { const configPaths = [ diff --git a/packages-internal/bundle-size-checker/src/create.js b/packages-internal/bundle-size-checker/src/create.js index 769bc98dd1c7e9..a534d74e432985 100644 --- a/packages-internal/bundle-size-checker/src/create.js +++ b/packages-internal/bundle-size-checker/src/create.js @@ -6,6 +6,7 @@ import fse from 'fs-extra'; import yargs from 'yargs'; import Piscina from 'piscina'; import { loadConfig } from './configLoader.js'; +import { uploadSnapshot } from './uploadSnapshot.js'; const MAX_CONCURRENCY = Math.min(8, os.cpus().length); @@ -14,9 +15,10 @@ const rootDir = process.cwd(); /** * creates size snapshot for every bundle that built with webpack * @param {CommandLineArgs} args + * @param {BundleSizeCheckerConfig} config - The loaded configuration * @returns {Promise>} */ -async function getWebpackSizes(args) { +async function getWebpackSizes(args, config) { const worker = new Piscina({ filename: new URL('./worker.js', import.meta.url).href, maxThreads: MAX_CONCURRENCY, @@ -25,8 +27,6 @@ async function getWebpackSizes(args) { const buildDir = path.join(rootDir, 'build'); await fse.emptyDir(buildDir); - const config = await loadConfig(rootDir); - if ( !config || !config.entrypoints || @@ -59,8 +59,10 @@ async function run(argv) { const snapshotDestPath = output ? path.resolve(output) : path.join(rootDir, 'size-snapshot.json'); + const config = await loadConfig(rootDir); + const bundleSizes = Object.fromEntries([ - ...(await getWebpackSizes({ analyze, accurateBundles })), + ...(await getWebpackSizes({ analyze, accurateBundles }, config)), ]); // Ensure output directory exists @@ -69,6 +71,21 @@ async function run(argv) { // eslint-disable-next-line no-console console.log(`Bundle size snapshot written to ${snapshotDestPath}`); + + // Upload the snapshot if upload configuration is provided + if (config && config.upload) { + try { + // eslint-disable-next-line no-console + console.log('Uploading bundle size snapshot to S3...'); + const { key } = await uploadSnapshot(snapshotDestPath, config.upload); + // eslint-disable-next-line no-console + console.log(`Bundle size snapshot uploaded to S3 with key: ${key}`); + } catch (/** @type {any} */ error) { + console.error('Failed to upload bundle size snapshot:', error.message); + // Exit with error code to indicate failure + process.exit(1); + } + } } yargs(process.argv.slice(2)) diff --git a/packages-internal/bundle-size-checker/src/types.d.ts b/packages-internal/bundle-size-checker/src/types.d.ts index e9563ce451ffe4..04f35fbf95bc5b 100644 --- a/packages-internal/bundle-size-checker/src/types.d.ts +++ b/packages-internal/bundle-size-checker/src/types.d.ts @@ -27,6 +27,19 @@ interface WebpackStats { }; } +// Upload configuration +interface UploadConfig { + project: string; // The project name (e.g., "mui/material-ui") + branch?: string; // Optional branch name (defaults to current Git branch) + isPullRequest?: boolean; // Whether this is a pull request build (defaults to false) +} + +// Bundle size checker config +interface BundleSizeCheckerConfig { + entrypoints: string[]; + upload?: UploadConfig; +} + // Command line argument types interface CommandLineArgs { analyze?: boolean; diff --git a/packages-internal/bundle-size-checker/src/uploadSnapshot.js b/packages-internal/bundle-size-checker/src/uploadSnapshot.js new file mode 100644 index 00000000000000..cedb29cc5a00e6 --- /dev/null +++ b/packages-internal/bundle-size-checker/src/uploadSnapshot.js @@ -0,0 +1,89 @@ +import fs from 'fs'; +import { S3Client, PutObjectCommand, PutObjectTaggingCommand } from '@aws-sdk/client-s3'; +import { execa } from 'execa'; +import { fromEnv } from '@aws-sdk/credential-providers'; + +/** + * Gets the current Git branch name + * @returns {Promise} The current branch name + */ +async function getCurrentBranch() { + try { + const { stdout } = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD']); + return stdout.trim(); + } catch (/** @type {any} */ error) { + console.warn('Failed to determine Git branch:', error); + return 'unknown-branch'; + } +} + +/** + * Gets the current Git commit SHA + * @returns {Promise} The current commit SHA + */ +async function getCurrentCommitSHA() { + const { stdout } = await execa('git', ['rev-parse', 'HEAD']); + return stdout.trim(); +} + +/** + * Uploads the size snapshot to S3 + * @param {string} snapshotPath - The path to the size snapshot JSON file + * @param {UploadConfig} uploadConfig - The upload configuration + * @param {string} [commitSha] - Optional commit SHA (defaults to current Git HEAD) + * @returns {Promise<{key:string}>} + */ +export async function uploadSnapshot(snapshotPath, uploadConfig, commitSha) { + if (!uploadConfig || !uploadConfig.project) { + throw new Error('Upload configuration is missing or invalid'); + } + + // Run git operations and file reading in parallel + const [sha, branch, fileContent] = await Promise.all([ + // Get the current commit SHA if not provided + commitSha || getCurrentCommitSHA(), + // Get branch name if not provided + uploadConfig.branch || getCurrentBranch(), + // Read the snapshot file + fs.promises.readFile(snapshotPath), + ]); + + // Default isPullRequest is false + const isPullRequest = uploadConfig.isPullRequest || false; + + // Create S3 client (uses AWS credentials from environment) + const client = new S3Client({ + region: process.env.AWS_REGION_ARTIFACTS || process.env.AWS_REGION || 'eu-central-1', + credentials: fromEnv(), + }); + + // S3 bucket and key + const bucket = 'mui-org-ci'; + const key = `artifacts/${uploadConfig.project}/${sha}/size-snapshot.json`; + + // Upload the file first + await client.send( + new PutObjectCommand({ + Bucket: bucket, + Key: key, + Body: fileContent, + ContentType: 'application/json', + }), + ); + + // Then add tags to the uploaded object + await client.send( + new PutObjectTaggingCommand({ + Bucket: bucket, + Key: key, + Tagging: { + TagSet: [ + { Key: 'isPullRequest', Value: isPullRequest ? 'yes' : 'no' }, + { Key: 'branch', Value: branch }, + ], + }, + }), + ); + + return { key }; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36c1b0718b9037..3983187e83b7bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1072,9 +1072,18 @@ importers: packages-internal/bundle-size-checker: dependencies: + '@aws-sdk/client-s3': + specifier: ^3.515.0 + version: 3.787.0 + '@aws-sdk/credential-providers': + specifier: ^3.787.0 + version: 3.787.0 compression-webpack-plugin: specifier: ^10.0.0 version: 10.0.0(webpack@5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))) + execa: + specifier: ^7.2.0 + version: 7.2.0 fast-glob: specifier: ^3.3.2 version: 3.3.3 @@ -2460,6 +2469,169 @@ packages: '@asamuzakjp/css-color@2.8.3': resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==} + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-cognito-identity@3.787.0': + resolution: {integrity: sha512-7v6nywZ5wcQxX7qdZ5M1ld15QdkzLU6fAKiEqbvJKu4dM8cFW6As+DbS990Mg46pp1xM/yvme+51xZDTfTfJZA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/client-s3@3.787.0': + resolution: {integrity: sha512-eGLCWkN0NlntJ9yPU6OKUggVS4cFvuZJog+cFg1KD5hniLqz7Y0YRtB4uBxW212fK3XCfddgyscEOEeHaTQQTw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/client-sso@3.787.0': + resolution: {integrity: sha512-L8R+Mh258G0DC73ktpSVrG4TT9i2vmDLecARTDR/4q5sRivdDQSL5bUp3LKcK80Bx+FRw3UETIlX6mYMLL9PJQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/core@3.775.0': + resolution: {integrity: sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-cognito-identity@3.787.0': + resolution: {integrity: sha512-nF5XjgvZHFuyttOeTjMgfEsg6slZPQ6uI34yzq12Kq4icFgcD4bQsijnQClMN7A0u5qR8Ad8kume4b7+I2++Ig==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-env@3.775.0': + resolution: {integrity: sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-http@3.775.0': + resolution: {integrity: sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-ini@3.787.0': + resolution: {integrity: sha512-hc2taRoDlXn2uuNuHWDJljVWYrp3r9JF1a/8XmOAZhVUNY+ImeeStylHXhXXKEA4JOjW+5PdJj0f1UDkVCHJiQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-node@3.787.0': + resolution: {integrity: sha512-JioVi44B1vDMaK2CdzqimwvJD3uzvzbQhaEWXsGMBcMcNHajXAXf08EF50JG3ZhLrhhUsT1ObXpbTaPINOhh+g==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-process@3.775.0': + resolution: {integrity: sha512-A6k68H9rQp+2+7P7SGO90Csw6nrUEm0Qfjpn9Etc4EboZhhCLs9b66umUsTsSBHus4FDIe5JQxfCUyt1wgNogg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-sso@3.787.0': + resolution: {integrity: sha512-fHc08bsvwm4+dEMEQKnQ7c1irEQmmxbgS+Fq41y09pPvPh31nAhoMcjBSTWAaPHvvsRbTYvmP4Mf12ZGr8/nfg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.787.0': + resolution: {integrity: sha512-SobmCwNbk6TfEsF283mZPQEI5vV2j6eY5tOCj8Er4Lzraxu9fBPADV+Bib2A8F6jlB1lMPJzOuDCbEasSt/RIw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-providers@3.787.0': + resolution: {integrity: sha512-kR3RtI7drOc9pho13vWbUC2Bvrx9A0G4iizBDGmTs08NOdg4w3c1I4kdLG9tyPiIMeVnH+wYrsli5CM7xIfqiA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-bucket-endpoint@3.775.0': + resolution: {integrity: sha512-qogMIpVChDYr4xiUNC19/RDSw/sKoHkAhouS6Skxiy6s27HBhow1L3Z1qVYXuBmOZGSWPU0xiyZCvOyWrv9s+Q==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-expect-continue@3.775.0': + resolution: {integrity: sha512-Apd3owkIeUW5dnk3au9np2IdW2N0zc9NjTjHiH+Mx3zqwSrc+m+ANgJVgk9mnQjMzU/vb7VuxJ0eqdEbp5gYsg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.787.0': + resolution: {integrity: sha512-X71qEwWoixFmwowWzlPoZUR3u1CWJ7iAzU0EzIxqmPhQpQJLFmdL1+SRjqATynDPZQzLs1a5HBtPT++EnZ+Quw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-host-header@3.775.0': + resolution: {integrity: sha512-tkSegM0Z6WMXpLB8oPys/d+umYIocvO298mGvcMCncpRl77L9XkvSLJIFzaHes+o7djAgIduYw8wKIMStFss2w==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-location-constraint@3.775.0': + resolution: {integrity: sha512-8TMXEHZXZTFTckQLyBT5aEI8fX11HZcwZseRifvBKKpj0RZDk4F0EEYGxeNSPpUQ7n+PRWyfAEnnZNRdAj/1NQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-logger@3.775.0': + resolution: {integrity: sha512-FaxO1xom4MAoUJsldmR92nT1G6uZxTdNYOFYtdHfd6N2wcNaTuxgjIvqzg5y7QIH9kn58XX/dzf1iTjgqUStZw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.775.0': + resolution: {integrity: sha512-GLCzC8D0A0YDG5u3F5U03Vb9j5tcOEFhr8oc6PDk0k0vm5VwtZOE6LvK7hcCSoAB4HXyOUM0sQuXrbaAh9OwXA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.775.0': + resolution: {integrity: sha512-zsvcu7cWB28JJ60gVvjxPCI7ZU7jWGcpNACPiZGyVtjYXwcxyhXbYEVDSWKsSA6ERpz9XrpLYod8INQWfW3ECg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-ssec@3.775.0': + resolution: {integrity: sha512-Iw1RHD8vfAWWPzBBIKaojO4GAvQkHOYIpKdAfis/EUSUmSa79QsnXnRqsdcE0mCB0Ylj23yi+ah4/0wh9FsekA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-user-agent@3.787.0': + resolution: {integrity: sha512-Lnfj8SmPLYtrDFthNIaNj66zZsBCam+E4XiUDr55DIHTGstH6qZ/q6vg0GfbukxwSmUcGMwSR4Qbn8rb8yd77g==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/nested-clients@3.787.0': + resolution: {integrity: sha512-xk03q1xpKNHgbuo+trEf1dFrI239kuMmjKKsqLEsHlAZbuFq4yRGMlHBrVMnKYOPBhVFDS/VineM991XI52fKg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/region-config-resolver@3.775.0': + resolution: {integrity: sha512-40iH3LJjrQS3LKUJAl7Wj0bln7RFPEvUYKFxtP8a+oKFDO0F65F52xZxIJbPn6sHkxWDAnZlGgdjZXM3p2g5wQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.775.0': + resolution: {integrity: sha512-cnGk8GDfTMJ8p7+qSk92QlIk2bmTmFJqhYxcXZ9PysjZtx0xmfCMxnG3Hjy1oU2mt5boPCVSOptqtWixayM17g==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/token-providers@3.787.0': + resolution: {integrity: sha512-d7/NIqxq308Zg0RPMNrmn0QvzniL4Hx8Qdwzr6YZWLYAbUSvZYS2ppLR3BFWSkV6SsTJUx8BuDaj3P8vttkrog==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/types@3.775.0': + resolution: {integrity: sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-arn-parser@3.723.0': + resolution: {integrity: sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-endpoints@3.787.0': + resolution: {integrity: sha512-fd3zkiOkwnbdbN0Xp9TsP5SWrmv0SpT70YEdbb8wAj2DWQwiCmFszaSs+YCvhoCdmlR3Wl9Spu0pGpSAGKeYvQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-locate-window@3.723.0': + resolution: {integrity: sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-user-agent-browser@3.775.0': + resolution: {integrity: sha512-txw2wkiJmZKVdDbscK7VBK+u+TJnRtlUjRTLei+elZg2ADhpQxfVAQl436FUeIv6AhB/oRHW6/K/EAGXUSWi0A==} + + '@aws-sdk/util-user-agent-node@3.787.0': + resolution: {integrity: sha512-mG7Lz8ydfG4SF9e8WSXiPQ/Lsn3n8A5B5jtPROidafi06I3ckV2WxyMLdwG14m919NoS6IOfWHyRGSqWIwbVKA==} + engines: {node: '>=18.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.775.0': + resolution: {integrity: sha512-b9NGO6FKJeLGYnV7Z1yvcP1TNU4dkD5jNsLWOF1/sygZoASaQhNOlaiJ/1OH331YQ1R1oWk38nBb0frsYkDsOQ==} + engines: {node: '>=18.0.0'} + '@babel/cli@7.27.0': resolution: {integrity: sha512-bZfxn8DRxwiVzDO5CEeV+7IqXeCkzI4yYnrQbpwjT76CUyossQc6RYE7n+xfm0/2k40lPaCpW0FhxYs7EBAetw==} engines: {node: '>=6.9.0'} @@ -5525,6 +5697,218 @@ packages: resolution: {integrity: sha512-d4SdG+6UmGdzWw38a4sN3lF/nTEzsDxhzU13wm10ejOpPehtmRoqBKnPztQUfFiWbNvSb4czkWYJD4kt+5+Fuw==} engines: {node: '>= 18', npm: '>= 8.6.0'} + '@smithy/abort-controller@4.0.2': + resolution: {integrity: sha512-Sl/78VDtgqKxN2+1qduaVE140XF+Xg+TafkncspwM4jFP/LHr76ZHmIY/y3V1M0mMLNk+Je6IGbzxy23RSToMw==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader-native@4.0.0': + resolution: {integrity: sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==} + engines: {node: '>=18.0.0'} + + '@smithy/chunked-blob-reader@5.0.0': + resolution: {integrity: sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==} + engines: {node: '>=18.0.0'} + + '@smithy/config-resolver@4.1.0': + resolution: {integrity: sha512-8smPlwhga22pwl23fM5ew4T9vfLUCeFXlcqNOCD5M5h8VmNPNUE9j6bQSuRXpDSV11L/E/SwEBQuW8hr6+nS1A==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.2.0': + resolution: {integrity: sha512-k17bgQhVZ7YmUvA8at4af1TDpl0NDMBuBKJl8Yg0nrefwmValU+CnA5l/AriVdQNthU/33H3nK71HrLgqOPr1Q==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.0.2': + resolution: {integrity: sha512-32lVig6jCaWBHnY+OEQ6e6Vnt5vDHaLiydGrwYMW9tPqO688hPGTYRamYJ1EptxEC2rAwJrHWmPoKRBl4iTa8w==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-codec@4.0.2': + resolution: {integrity: sha512-p+f2kLSK7ZrXVfskU/f5dzksKTewZk8pJLPvER3aFHPt76C2MxD9vNatSfLzzQSQB4FNO96RK4PSXfhD1TTeMQ==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-browser@4.0.2': + resolution: {integrity: sha512-CepZCDs2xgVUtH7ZZ7oDdZFH8e6Y2zOv8iiX6RhndH69nlojCALSKK+OXwZUgOtUZEUaZ5e1hULVCHYbCn7pug==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-config-resolver@4.1.0': + resolution: {integrity: sha512-1PI+WPZ5TWXrfj3CIoKyUycYynYJgZjuQo8U+sphneOtjsgrttYybdqESFReQrdWJ+LKt6NEdbYzmmfDBmjX2A==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-node@4.0.2': + resolution: {integrity: sha512-C5bJ/C6x9ENPMx2cFOirspnF9ZsBVnBMtP6BdPl/qYSuUawdGQ34Lq0dMcf42QTjUZgWGbUIZnz6+zLxJlb9aw==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-universal@4.0.2': + resolution: {integrity: sha512-St8h9JqzvnbB52FtckiHPN4U/cnXcarMniXRXTKn0r4b4XesZOGiAyUdj1aXbqqn1icSqBlzzUsCl6nPB018ng==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.0.2': + resolution: {integrity: sha512-+9Dz8sakS9pe7f2cBocpJXdeVjMopUDLgZs1yWeu7h++WqSbjUYv/JAJwKwXw1HV6gq1jyWjxuyn24E2GhoEcQ==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-blob-browser@4.0.2': + resolution: {integrity: sha512-3g188Z3DyhtzfBRxpZjU8R9PpOQuYsbNnyStc/ZVS+9nVX1f6XeNOa9IrAh35HwwIZg+XWk8bFVtNINVscBP+g==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.0.2': + resolution: {integrity: sha512-VnTpYPnRUE7yVhWozFdlxcYknv9UN7CeOqSrMH+V877v4oqtVYuoqhIhtSjmGPvYrYnAkaM61sLMKHvxL138yg==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-stream-node@4.0.2': + resolution: {integrity: sha512-POWDuTznzbIwlEXEvvXoPMS10y0WKXK790soe57tFRfvf4zBHyzE529HpZMqmDdwG9MfFflnyzndUQ8j78ZdSg==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.0.2': + resolution: {integrity: sha512-GatB4+2DTpgWPday+mnUkoumP54u/MDM/5u44KF9hIu8jF0uafZtQLcdfIKkIcUNuF/fBojpLEHZS/56JqPeXQ==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.0.0': + resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==} + engines: {node: '>=18.0.0'} + + '@smithy/md5-js@4.0.2': + resolution: {integrity: sha512-Hc0R8EiuVunUewCse2syVgA2AfSRco3LyAv07B/zCOMa+jpXI9ll+Q21Nc6FAlYPcpNcAXqBzMhNs1CD/pP2bA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.0.2': + resolution: {integrity: sha512-hAfEXm1zU+ELvucxqQ7I8SszwQ4znWMbNv6PLMndN83JJN41EPuS93AIyh2N+gJ6x8QFhzSO6b7q2e6oClDI8A==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.1.0': + resolution: {integrity: sha512-xhLimgNCbCzsUppRTGXWkZywksuTThxaIB0HwbpsVLY5sceac4e1TZ/WKYqufQLaUy+gUSJGNdwD2jo3cXL0iA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.1.0': + resolution: {integrity: sha512-2zAagd1s6hAaI/ap6SXi5T3dDwBOczOMCSkkYzktqN1+tzbk1GAsHNAdo/1uzxz3Ky02jvZQwbi/vmDA6z4Oyg==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.0.3': + resolution: {integrity: sha512-rfgDVrgLEVMmMn0BI8O+8OVr6vXzjV7HZj57l0QxslhzbvVfikZbVfBVthjLHqib4BW44QhcIgJpvebHlRaC9A==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.0.2': + resolution: {integrity: sha512-eSPVcuJJGVYrFYu2hEq8g8WWdJav3sdrI4o2c6z/rjnYDd3xH9j9E7deZQCzFn4QvGPouLngH3dQ+QVTxv5bOQ==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.0.2': + resolution: {integrity: sha512-WgCkILRZfJwJ4Da92a6t3ozN/zcvYyJGUTmfGbgS/FkCcoCjl7G4FJaCDN1ySdvLvemnQeo25FdkyMSTSwulsw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.0.4': + resolution: {integrity: sha512-/mdqabuAT3o/ihBGjL94PUbTSPSRJ0eeVTdgADzow0wRJ0rN4A27EOrtlK56MYiO1fDvlO3jVTCxQtQmK9dZ1g==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.0.2': + resolution: {integrity: sha512-wNRoQC1uISOuNc2s4hkOYwYllmiyrvVXWMtq+TysNRVQaHm4yoafYQyjN/goYZS+QbYlPIbb/QRjaUZMuzwQ7A==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.1.0': + resolution: {integrity: sha512-KxAOL1nUNw2JTYrtviRRjEnykIDhxc84qMBzxvu1MUfQfHTuBlCG7PA6EdVwqpJjH7glw7FqQoFxUJSyBQgu7g==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.0.2': + resolution: {integrity: sha512-NTOs0FwHw1vimmQM4ebh+wFQvOwkEf/kQL6bSM1Lock+Bv4I89B3hGYoUEPkmvYPkDKyp5UdXJYu+PoTQ3T31Q==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.0.2': + resolution: {integrity: sha512-v6w8wnmZcVXjfVLjxw8qF7OwESD9wnpjp0Dqry/Pod0/5vcEA3qxCr+BhbOHlxS8O+29eLpT3aagxXGwIoEk7Q==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.0.2': + resolution: {integrity: sha512-LA86xeFpTKn270Hbkixqs5n73S+LVM0/VZco8dqd+JT75Dyx3Lcw/MraL7ybjmz786+160K8rPOmhsq0SocoJQ==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.0.2': + resolution: {integrity: sha512-J9/gTWBGVuFZ01oVA6vdb4DAjf1XbDhK6sLsu3OS9qmLrS6KB5ygpeHiM3miIbj1qgSJ96GYszXFWv6ErJ8QEw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.0.2': + resolution: {integrity: sha512-Mz+mc7okA73Lyz8zQKJNyr7lIcHLiPYp0+oiqiMNc/t7/Kf2BENs5d63pEj7oPqdjaum6g0Fc8wC78dY1TgtXw==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.2.0': + resolution: {integrity: sha512-Qs65/w30pWV7LSFAez9DKy0Koaoh3iHhpcpCCJ4waj/iqwsuSzJna2+vYwq46yBaqO5ZbP9TjUsATUNxrKeBdw==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.2.0': + resolution: {integrity: sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.0.2': + resolution: {integrity: sha512-Bm8n3j2ScqnT+kJaClSVCMeiSenK6jVAzZCNewsYWuZtnBehEz4r2qP0riZySZVfzB+03XZHJeqfmJDkeeSLiQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.0.0': + resolution: {integrity: sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.0.0': + resolution: {integrity: sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.0.0': + resolution: {integrity: sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.0.0': + resolution: {integrity: sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.0.0': + resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.0.8': + resolution: {integrity: sha512-ZTypzBra+lI/LfTYZeop9UjoJhhGRTg3pxrNpfSTQLd3AJ37r2z4AXTKpq1rFXiiUIJsYyFgNJdjWRGP/cbBaQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.0.8': + resolution: {integrity: sha512-Rgk0Jc/UDfRTzVthye/k2dDsz5Xxs9LZaKCNPgJTRyoyBoeiNCnHsYGOyu1PKN+sDyPnJzMOz22JbwxzBp9NNA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.0.2': + resolution: {integrity: sha512-6QSutU5ZyrpNbnd51zRTL7goojlcnuOB55+F9VBD+j8JpRY50IGamsjlycrmpn8PQkmJucFW8A0LSfXj7jjtLQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.0.0': + resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.0.2': + resolution: {integrity: sha512-6GDamTGLuBQVAEuQ4yDQ+ti/YINf/MEmIegrEeg7DdB/sld8BX1lqt9RRuIcABOhAGTA50bRbPzErez7SlDtDQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.0.2': + resolution: {integrity: sha512-Qryc+QG+7BCpvjloFLQrmlSd0RsVRHejRXd78jNO3+oREueCjwG1CCEH1vduw/ZkM1U9TztwIKVIi3+8MJScGg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.2.0': + resolution: {integrity: sha512-Vj1TtwWnuWqdgQI6YTUF5hQ/0jmFiOYsc51CSMgj7QfyO+RF4EnT2HNjoviNlOOmgzgvf3f5yno+EiC4vrnaWQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.0.0': + resolution: {integrity: sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.0.0': + resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==} + engines: {node: '>=18.0.0'} + + '@smithy/util-waiter@4.0.3': + resolution: {integrity: sha512-JtaY3FxmD+te+KSI2FJuEcfNC9T/DGGVf551babM7fAaXhjJUt7oSYurH1Devxd2+BOSUACCgt3buinx4UnmEA==} + engines: {node: '>=18.0.0'} + '@socket.io/component-emitter@3.1.0': resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} @@ -6759,6 +7143,9 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bowser@2.11.0: + resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + boxen@7.0.0: resolution: {integrity: sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==} engines: {node: '>=14.16'} @@ -8333,6 +8720,10 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} + execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -8405,6 +8796,10 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-xml-parser@4.4.1: + resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} + hasBin: true + fast-xml-parser@4.5.3: resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==} hasBin: true @@ -9057,6 +9452,10 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} @@ -14106,6 +14505,542 @@ snapshots: '@csstools/css-tokenizer': 3.0.3 lru-cache: 10.4.3 + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.775.0 + tslib: 2.8.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.775.0 + tslib: 2.8.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-locate-window': 3.723.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-locate-window': 3.723.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.775.0 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-cognito-identity@3.787.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/credential-provider-node': 3.787.0 + '@aws-sdk/middleware-host-header': 3.775.0 + '@aws-sdk/middleware-logger': 3.775.0 + '@aws-sdk/middleware-recursion-detection': 3.775.0 + '@aws-sdk/middleware-user-agent': 3.787.0 + '@aws-sdk/region-config-resolver': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.787.0 + '@aws-sdk/util-user-agent-browser': 3.775.0 + '@aws-sdk/util-user-agent-node': 3.787.0 + '@smithy/config-resolver': 4.1.0 + '@smithy/core': 3.2.0 + '@smithy/fetch-http-handler': 5.0.2 + '@smithy/hash-node': 4.0.2 + '@smithy/invalid-dependency': 4.0.2 + '@smithy/middleware-content-length': 4.0.2 + '@smithy/middleware-endpoint': 4.1.0 + '@smithy/middleware-retry': 4.1.0 + '@smithy/middleware-serde': 4.0.3 + '@smithy/middleware-stack': 4.0.2 + '@smithy/node-config-provider': 4.0.2 + '@smithy/node-http-handler': 4.0.4 + '@smithy/protocol-http': 5.1.0 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/url-parser': 4.0.2 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.8 + '@smithy/util-defaults-mode-node': 4.0.8 + '@smithy/util-endpoints': 3.0.2 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-retry': 4.0.2 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-s3@3.787.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/credential-provider-node': 3.787.0 + '@aws-sdk/middleware-bucket-endpoint': 3.775.0 + '@aws-sdk/middleware-expect-continue': 3.775.0 + '@aws-sdk/middleware-flexible-checksums': 3.787.0 + '@aws-sdk/middleware-host-header': 3.775.0 + '@aws-sdk/middleware-location-constraint': 3.775.0 + '@aws-sdk/middleware-logger': 3.775.0 + '@aws-sdk/middleware-recursion-detection': 3.775.0 + '@aws-sdk/middleware-sdk-s3': 3.775.0 + '@aws-sdk/middleware-ssec': 3.775.0 + '@aws-sdk/middleware-user-agent': 3.787.0 + '@aws-sdk/region-config-resolver': 3.775.0 + '@aws-sdk/signature-v4-multi-region': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.787.0 + '@aws-sdk/util-user-agent-browser': 3.775.0 + '@aws-sdk/util-user-agent-node': 3.787.0 + '@aws-sdk/xml-builder': 3.775.0 + '@smithy/config-resolver': 4.1.0 + '@smithy/core': 3.2.0 + '@smithy/eventstream-serde-browser': 4.0.2 + '@smithy/eventstream-serde-config-resolver': 4.1.0 + '@smithy/eventstream-serde-node': 4.0.2 + '@smithy/fetch-http-handler': 5.0.2 + '@smithy/hash-blob-browser': 4.0.2 + '@smithy/hash-node': 4.0.2 + '@smithy/hash-stream-node': 4.0.2 + '@smithy/invalid-dependency': 4.0.2 + '@smithy/md5-js': 4.0.2 + '@smithy/middleware-content-length': 4.0.2 + '@smithy/middleware-endpoint': 4.1.0 + '@smithy/middleware-retry': 4.1.0 + '@smithy/middleware-serde': 4.0.3 + '@smithy/middleware-stack': 4.0.2 + '@smithy/node-config-provider': 4.0.2 + '@smithy/node-http-handler': 4.0.4 + '@smithy/protocol-http': 5.1.0 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/url-parser': 4.0.2 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.8 + '@smithy/util-defaults-mode-node': 4.0.8 + '@smithy/util-endpoints': 3.0.2 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-retry': 4.0.2 + '@smithy/util-stream': 4.2.0 + '@smithy/util-utf8': 4.0.0 + '@smithy/util-waiter': 4.0.3 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso@3.787.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/middleware-host-header': 3.775.0 + '@aws-sdk/middleware-logger': 3.775.0 + '@aws-sdk/middleware-recursion-detection': 3.775.0 + '@aws-sdk/middleware-user-agent': 3.787.0 + '@aws-sdk/region-config-resolver': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.787.0 + '@aws-sdk/util-user-agent-browser': 3.775.0 + '@aws-sdk/util-user-agent-node': 3.787.0 + '@smithy/config-resolver': 4.1.0 + '@smithy/core': 3.2.0 + '@smithy/fetch-http-handler': 5.0.2 + '@smithy/hash-node': 4.0.2 + '@smithy/invalid-dependency': 4.0.2 + '@smithy/middleware-content-length': 4.0.2 + '@smithy/middleware-endpoint': 4.1.0 + '@smithy/middleware-retry': 4.1.0 + '@smithy/middleware-serde': 4.0.3 + '@smithy/middleware-stack': 4.0.2 + '@smithy/node-config-provider': 4.0.2 + '@smithy/node-http-handler': 4.0.4 + '@smithy/protocol-http': 5.1.0 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/url-parser': 4.0.2 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.8 + '@smithy/util-defaults-mode-node': 4.0.8 + '@smithy/util-endpoints': 3.0.2 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-retry': 4.0.2 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.775.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/core': 3.2.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/property-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/signature-v4': 5.0.2 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/util-middleware': 4.0.2 + fast-xml-parser: 4.4.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-cognito-identity@3.787.0': + dependencies: + '@aws-sdk/client-cognito-identity': 3.787.0 + '@aws-sdk/types': 3.775.0 + '@smithy/property-provider': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-env@3.775.0': + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@smithy/property-provider': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.775.0': + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@smithy/fetch-http-handler': 5.0.2 + '@smithy/node-http-handler': 4.0.4 + '@smithy/property-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/util-stream': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.787.0': + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/credential-provider-env': 3.775.0 + '@aws-sdk/credential-provider-http': 3.775.0 + '@aws-sdk/credential-provider-process': 3.775.0 + '@aws-sdk/credential-provider-sso': 3.787.0 + '@aws-sdk/credential-provider-web-identity': 3.787.0 + '@aws-sdk/nested-clients': 3.787.0 + '@aws-sdk/types': 3.775.0 + '@smithy/credential-provider-imds': 4.0.2 + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.787.0': + dependencies: + '@aws-sdk/credential-provider-env': 3.775.0 + '@aws-sdk/credential-provider-http': 3.775.0 + '@aws-sdk/credential-provider-ini': 3.787.0 + '@aws-sdk/credential-provider-process': 3.775.0 + '@aws-sdk/credential-provider-sso': 3.787.0 + '@aws-sdk/credential-provider-web-identity': 3.787.0 + '@aws-sdk/types': 3.775.0 + '@smithy/credential-provider-imds': 4.0.2 + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.775.0': + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.787.0': + dependencies: + '@aws-sdk/client-sso': 3.787.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/token-providers': 3.787.0 + '@aws-sdk/types': 3.775.0 + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.787.0': + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/nested-clients': 3.787.0 + '@aws-sdk/types': 3.775.0 + '@smithy/property-provider': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-providers@3.787.0': + dependencies: + '@aws-sdk/client-cognito-identity': 3.787.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/credential-provider-cognito-identity': 3.787.0 + '@aws-sdk/credential-provider-env': 3.775.0 + '@aws-sdk/credential-provider-http': 3.775.0 + '@aws-sdk/credential-provider-ini': 3.787.0 + '@aws-sdk/credential-provider-node': 3.787.0 + '@aws-sdk/credential-provider-process': 3.775.0 + '@aws-sdk/credential-provider-sso': 3.787.0 + '@aws-sdk/credential-provider-web-identity': 3.787.0 + '@aws-sdk/nested-clients': 3.787.0 + '@aws-sdk/types': 3.775.0 + '@smithy/config-resolver': 4.1.0 + '@smithy/core': 3.2.0 + '@smithy/credential-provider-imds': 4.0.2 + '@smithy/node-config-provider': 4.0.2 + '@smithy/property-provider': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/middleware-bucket-endpoint@3.775.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-arn-parser': 3.723.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + '@smithy/util-config-provider': 4.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-expect-continue@3.775.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.787.0': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@smithy/is-array-buffer': 4.0.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-stream': 4.2.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.775.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-location-constraint@3.775.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.775.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.775.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.775.0': + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-arn-parser': 3.723.0 + '@smithy/core': 3.2.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/signature-v4': 5.0.2 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-stream': 4.2.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-ssec@3.775.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.787.0': + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.787.0 + '@smithy/core': 3.2.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.787.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/middleware-host-header': 3.775.0 + '@aws-sdk/middleware-logger': 3.775.0 + '@aws-sdk/middleware-recursion-detection': 3.775.0 + '@aws-sdk/middleware-user-agent': 3.787.0 + '@aws-sdk/region-config-resolver': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.787.0 + '@aws-sdk/util-user-agent-browser': 3.775.0 + '@aws-sdk/util-user-agent-node': 3.787.0 + '@smithy/config-resolver': 4.1.0 + '@smithy/core': 3.2.0 + '@smithy/fetch-http-handler': 5.0.2 + '@smithy/hash-node': 4.0.2 + '@smithy/invalid-dependency': 4.0.2 + '@smithy/middleware-content-length': 4.0.2 + '@smithy/middleware-endpoint': 4.1.0 + '@smithy/middleware-retry': 4.1.0 + '@smithy/middleware-serde': 4.0.3 + '@smithy/middleware-stack': 4.0.2 + '@smithy/node-config-provider': 4.0.2 + '@smithy/node-http-handler': 4.0.4 + '@smithy/protocol-http': 5.1.0 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/url-parser': 4.0.2 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.8 + '@smithy/util-defaults-mode-node': 4.0.8 + '@smithy/util-endpoints': 3.0.2 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-retry': 4.0.2 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.775.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/types': 4.2.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.2 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.775.0': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/signature-v4': 5.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.787.0': + dependencies: + '@aws-sdk/nested-clients': 3.787.0 + '@aws-sdk/types': 3.775.0 + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.775.0': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/util-arn-parser@3.723.0': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.787.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/types': 4.2.0 + '@smithy/util-endpoints': 3.0.2 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.723.0': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.775.0': + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/types': 4.2.0 + bowser: 2.11.0 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.787.0': + dependencies: + '@aws-sdk/middleware-user-agent': 3.787.0 + '@aws-sdk/types': 3.775.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.775.0': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + '@babel/cli@7.27.0(@babel/core@7.26.10)': dependencies: '@babel/core': 7.26.10 @@ -17563,6 +18498,337 @@ snapshots: transitivePeerDependencies: - debug + '@smithy/abort-controller@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader-native@4.0.0': + dependencies: + '@smithy/util-base64': 4.0.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader@5.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/config-resolver@4.1.0': + dependencies: + '@smithy/node-config-provider': 4.0.2 + '@smithy/types': 4.2.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.2 + tslib: 2.8.1 + + '@smithy/core@3.2.0': + dependencies: + '@smithy/middleware-serde': 4.0.3 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-stream': 4.2.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.0.2': + dependencies: + '@smithy/node-config-provider': 4.0.2 + '@smithy/property-provider': 4.0.2 + '@smithy/types': 4.2.0 + '@smithy/url-parser': 4.0.2 + tslib: 2.8.1 + + '@smithy/eventstream-codec@4.0.2': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.2.0 + '@smithy/util-hex-encoding': 4.0.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@4.0.2': + dependencies: + '@smithy/eventstream-serde-universal': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@4.1.0': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@4.0.2': + dependencies: + '@smithy/eventstream-serde-universal': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@4.0.2': + dependencies: + '@smithy/eventstream-codec': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.0.2': + dependencies: + '@smithy/protocol-http': 5.1.0 + '@smithy/querystring-builder': 4.0.2 + '@smithy/types': 4.2.0 + '@smithy/util-base64': 4.0.0 + tslib: 2.8.1 + + '@smithy/hash-blob-browser@4.0.2': + dependencies: + '@smithy/chunked-blob-reader': 5.0.0 + '@smithy/chunked-blob-reader-native': 4.0.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/hash-node@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@smithy/hash-stream-node@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/md5-js@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.0.2': + dependencies: + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.1.0': + dependencies: + '@smithy/core': 3.2.0 + '@smithy/middleware-serde': 4.0.3 + '@smithy/node-config-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + '@smithy/url-parser': 4.0.2 + '@smithy/util-middleware': 4.0.2 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.1.0': + dependencies: + '@smithy/node-config-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/service-error-classification': 4.0.2 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-retry': 4.0.2 + tslib: 2.8.1 + uuid: 9.0.1 + + '@smithy/middleware-serde@4.0.3': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.0.2': + dependencies: + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.0.4': + dependencies: + '@smithy/abort-controller': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/querystring-builder': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/property-provider@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/protocol-http@5.1.0': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + '@smithy/util-uri-escape': 4.0.0 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + + '@smithy/shared-ini-file-loader@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.0.2': + dependencies: + '@smithy/is-array-buffer': 4.0.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-uri-escape': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@smithy/smithy-client@4.2.0': + dependencies: + '@smithy/core': 3.2.0 + '@smithy/middleware-endpoint': 4.1.0 + '@smithy/middleware-stack': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + '@smithy/util-stream': 4.2.0 + tslib: 2.8.1 + + '@smithy/types@4.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.0.2': + dependencies: + '@smithy/querystring-parser': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-base64@4.0.0': + dependencies: + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.0.0': + dependencies: + '@smithy/is-array-buffer': 4.0.0 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.0.8': + dependencies: + '@smithy/property-provider': 4.0.2 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + bowser: 2.11.0 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.0.8': + dependencies: + '@smithy/config-resolver': 4.1.0 + '@smithy/credential-provider-imds': 4.0.2 + '@smithy/node-config-provider': 4.0.2 + '@smithy/property-provider': 4.0.2 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.0.2': + dependencies: + '@smithy/node-config-provider': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.0.2': + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-retry@4.0.2': + dependencies: + '@smithy/service-error-classification': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + + '@smithy/util-stream@4.2.0': + dependencies: + '@smithy/fetch-http-handler': 5.0.2 + '@smithy/node-http-handler': 4.0.4 + '@smithy/types': 4.2.0 + '@smithy/util-base64': 4.0.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@smithy/util-uri-escape@4.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.0.0': + dependencies: + '@smithy/util-buffer-from': 4.0.0 + tslib: 2.8.1 + + '@smithy/util-waiter@4.0.3': + dependencies: + '@smithy/abort-controller': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + '@socket.io/component-emitter@3.1.0': {} '@standard-schema/spec@1.0.0': {} @@ -19053,6 +20319,8 @@ snapshots: boolbase@1.0.0: {} + bowser@2.11.0: {} + boxen@7.0.0: dependencies: ansi-align: 3.0.1 @@ -21019,6 +22287,18 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 + execa@7.2.0: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + execa@8.0.1: dependencies: cross-spawn: 7.0.6 @@ -21177,6 +22457,10 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-xml-parser@4.4.1: + dependencies: + strnum: 1.1.2 + fast-xml-parser@4.5.3: dependencies: strnum: 1.1.2 @@ -21955,6 +23239,8 @@ snapshots: human-signals@2.1.0: {} + human-signals@4.3.1: {} + human-signals@5.0.0: {} human-signals@8.0.0: {} From cef12589042ace9e808acff3b2d0acb9bbceb544 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:30:33 +0200 Subject: [PATCH 15/18] update doc --- packages-internal/bundle-size-checker/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages-internal/bundle-size-checker/README.md b/packages-internal/bundle-size-checker/README.md index 1197cf4bb656ad..b703dfb299ee1b 100644 --- a/packages-internal/bundle-size-checker/README.md +++ b/packages-internal/bundle-size-checker/README.md @@ -34,8 +34,9 @@ export default defineConfig(async () => { return { entrypoints: [ // List of packages and components to measure - '@mui/material', - '@mui/material/Button', + '@mui/material', // Will bundle `import * as ... from '@mui/material'` + '@mui/material/Button', // Will bundle `import * as ... from '@mui/material/Button'` + '@mui/material#Button', // Will bundle `import Button from '@mui/material'` // ... ], // Optional upload configuration From ce33c253ff6b802213044fb4bb9ee64da3d829dc Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:55:40 +0200 Subject: [PATCH 16/18] fsh --- bundle-size/bundle-size-checker.config.mjs | 3 +-- packages-internal/bundle-size-checker/src/create.js | 5 ++--- packages-internal/bundle-size-checker/src/worker.js | 11 ++++++----- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/bundle-size/bundle-size-checker.config.mjs b/bundle-size/bundle-size-checker.config.mjs index 9d24bfaf173d10..c9a7d50c197126 100644 --- a/bundle-size/bundle-size-checker.config.mjs +++ b/bundle-size/bundle-size-checker.config.mjs @@ -29,8 +29,7 @@ export default defineConfig(async () => { return `@mui/lab/${componentName}`; }); - // Determine if we're in a CI environment and if it's a PR - const isPullRequest = process.env.CIRCLE_PULL_REQUEST ? true : false; + const isPullRequest = !!process.env.CIRCLE_PULL_REQUEST; // Return the complete entrypoints configuration return { diff --git a/packages-internal/bundle-size-checker/src/create.js b/packages-internal/bundle-size-checker/src/create.js index a534d74e432985..571d2ed7907973 100644 --- a/packages-internal/bundle-size-checker/src/create.js +++ b/packages-internal/bundle-size-checker/src/create.js @@ -61,9 +61,8 @@ async function run(argv) { const config = await loadConfig(rootDir); - const bundleSizes = Object.fromEntries([ - ...(await getWebpackSizes({ analyze, accurateBundles }, config)), - ]); + const webpackSizes = await getWebpackSizes({ analyze, accurateBundles }, config); + const bundleSizes = Object.fromEntries(webpackSizes.sort((a, b) => a[0].localeCompare(b[0]))); // Ensure output directory exists await fse.mkdirp(path.dirname(snapshotDestPath)); diff --git a/packages-internal/bundle-size-checker/src/worker.js b/packages-internal/bundle-size-checker/src/worker.js index a214b4c8c8b149..991bd221be1403 100644 --- a/packages-internal/bundle-size-checker/src/worker.js +++ b/packages-internal/bundle-size-checker/src/worker.js @@ -16,18 +16,19 @@ const rootDir = process.cwd(); /** * Creates webpack configuration for bundle size checking - * @param {string} entry - Entry point string + * @param {string} entryName - Entry point string * @param {CommandLineArgs} args * @returns {import('webpack').Configuration} */ -function createWebpackConfig(entry, args) { +function createWebpackConfig(entryName, args) { const analyzerMode = args.analyze ? 'static' : 'disabled'; const concatenateModules = !args.accurateBundles; - const entryName = entry; const [importSrc, importName] = entryName.split('#'); - const importSpec = importName ? `{ ${importName} as foo }` : '* as foo'; + const entryContent = importName + ? `import { ${importName} as foo } from '${importSrc}';console.log(foo);` + : `import * as foo from '${importSrc}';console.log(foo);`; /** * @type {import('webpack').Configuration} @@ -83,7 +84,7 @@ function createWebpackConfig(entry, args) { // This format is a data: url combined with inline matchResource to obtain a virtual entry. // See https://github.com/webpack/webpack/issues/6437#issuecomment-874466638 // See https://webpack.js.org/api/loaders/#inline-matchresource - [entryName]: `./index.js!=!data:text/javascript,import ${importSpec} from '${importSrc}';console.log(foo);`, + [entryName]: `./index.js!=!data:text/javascript,${entryContent}`, }, // TODO: 'browserslist:modern' // See https://github.com/webpack/webpack/issues/14203 From 64c63ad03ccb7894d47d8528dbfea4554704e647 Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Fri, 25 Apr 2025 12:35:46 +0200 Subject: [PATCH 17/18] use mui-public one --- bundle-size/package.json | 2 +- package.json | 2 +- .../bundle-size-checker/.eslintrc.cjs | 14 -- .../bundle-size-checker/README.md | 83 ------- .../bin/bundle-size-checker.js | 3 - .../bundle-size-checker/package.json | 32 --- .../bundle-size-checker/src/configLoader.js | 66 ----- .../bundle-size-checker/src/create.js | 119 --------- .../bundle-size-checker/src/defineConfig.js | 15 -- .../bundle-size-checker/src/fetchSnapshot.js | 21 -- .../bundle-size-checker/src/index.js | 7 - .../src/renderMarkdownReport.js | 232 ------------------ .../bundle-size-checker/src/sizeDiff.js | 199 --------------- .../bundle-size-checker/src/types.d.ts | 48 ---- .../bundle-size-checker/src/uploadSnapshot.js | 89 ------- .../bundle-size-checker/src/worker.js | 183 -------------- .../bundle-size-checker/tsconfig.json | 19 -- pnpm-lock.yaml | 109 +++----- 18 files changed, 33 insertions(+), 1210 deletions(-) delete mode 100644 packages-internal/bundle-size-checker/.eslintrc.cjs delete mode 100644 packages-internal/bundle-size-checker/README.md delete mode 100755 packages-internal/bundle-size-checker/bin/bundle-size-checker.js delete mode 100644 packages-internal/bundle-size-checker/package.json delete mode 100644 packages-internal/bundle-size-checker/src/configLoader.js delete mode 100644 packages-internal/bundle-size-checker/src/create.js delete mode 100644 packages-internal/bundle-size-checker/src/defineConfig.js delete mode 100644 packages-internal/bundle-size-checker/src/fetchSnapshot.js delete mode 100644 packages-internal/bundle-size-checker/src/index.js delete mode 100644 packages-internal/bundle-size-checker/src/renderMarkdownReport.js delete mode 100644 packages-internal/bundle-size-checker/src/sizeDiff.js delete mode 100644 packages-internal/bundle-size-checker/src/types.d.ts delete mode 100644 packages-internal/bundle-size-checker/src/uploadSnapshot.js delete mode 100644 packages-internal/bundle-size-checker/src/worker.js delete mode 100644 packages-internal/bundle-size-checker/tsconfig.json diff --git a/bundle-size/package.json b/bundle-size/package.json index 9a5cd3d47bbc5f..f204c2f700f5a2 100644 --- a/bundle-size/package.json +++ b/bundle-size/package.json @@ -7,7 +7,7 @@ "check": "NODE_OPTIONS=\"--max-old-space-size=4096\" bundle-size-checker --output ../size-snapshot.json" }, "dependencies": { - "@mui/internal-bundle-size-checker": "workspace:*", + "@mui/internal-bundle-size-checker": "github:mui/mui-public#bundle-size-checker&path:./packages/bundle-size-checker", "@mui/material": "workspace:*", "@mui/lab": "workspace:*", "@mui/private-theming": "workspace:*", diff --git a/package.json b/package.json index c2ecf5114d4f1e..e77eb1ebf8c820 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ }, "dependencies": { "@googleapis/sheets": "^9.6.0", - "@mui/internal-bundle-size-checker": "workspace:*", + "@mui/internal-bundle-size-checker": "github:mui/mui-public#bundle-size-checker&path:./packages/bundle-size-checker", "@netlify/functions": "^3.0.4", "@slack/bolt": "^4.2.1", "babel-plugin-transform-import-meta": "^2.3.2", diff --git a/packages-internal/bundle-size-checker/.eslintrc.cjs b/packages-internal/bundle-size-checker/.eslintrc.cjs deleted file mode 100644 index 41f130763b32d8..00000000000000 --- a/packages-internal/bundle-size-checker/.eslintrc.cjs +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - rules: { - 'import/prefer-default-export': 'off', - // Allow .js file extensions in import statements for ESM compatibility - 'import/extensions': [ - 'error', - 'ignorePackages', - { - js: 'always', - mjs: 'always', - }, - ], - }, -}; diff --git a/packages-internal/bundle-size-checker/README.md b/packages-internal/bundle-size-checker/README.md deleted file mode 100644 index b703dfb299ee1b..00000000000000 --- a/packages-internal/bundle-size-checker/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# Bundle Size Checker - -A tool to check and track the bundle size of MUI packages. - -## Features - -- Measures minified and gzipped bundle sizes of packages and components -- Compares bundle sizes between versions -- Generates markdown reports -- Uploads snapshots to S3 for persistent storage and comparison - -## Usage - -### CLI - -```bash -bundle-size-checker [options] -``` - -Options: - -- `--analyze`: Creates a webpack-bundle-analyzer report for each bundle -- `--accurateBundles`: Displays used bundles accurately at the cost of more CPU cycles -- `--output`, `-o`: Path to output the size snapshot JSON file - -### Configuration - -Create a `bundle-size-checker.config.js` or `bundle-size-checker.config.mjs` file: - -```js -import { defineConfig } from '@mui/internal-bundle-size-checker'; - -export default defineConfig(async () => { - return { - entrypoints: [ - // List of packages and components to measure - '@mui/material', // Will bundle `import * as ... from '@mui/material'` - '@mui/material/Button', // Will bundle `import * as ... from '@mui/material/Button'` - '@mui/material#Button', // Will bundle `import Button from '@mui/material'` - // ... - ], - // Optional upload configuration - upload: { - project: 'organization/repository', - branch: 'main', // Optional, defaults to current git branch - isPullRequest: false, // Optional, defaults to false - }, - }; -}); -``` - -### S3 Upload - -When the `upload` configuration is provided, the snapshot will be uploaded to S3 after generation. - -The snapshot will be uploaded to: - -```bash -s3://mui-org-ci/artifacts/{project}/{commit-sha}/size-snapshot.json -``` - -The following tags will be applied: - -- `isPullRequest`: 'yes' or 'no' -- `branch`: The branch name - -Required AWS environment variables: - -- `AWS_ACCESS_KEY_ID` or `AWS_ACCESS_KEY_ID_ARTIFACTS` -- `AWS_SECRET_ACCESS_KEY` or `AWS_SECRET_ACCESS_KEY_ARTIFACTS` -- `AWS_REGION` or `AWS_REGION_ARTIFACTS` (defaults to 'eu-central-1') - -If the upload fails, the CLI will exit with an error code. - -## API - -The library exports the following functions: - -- `defineConfig`: Helper for defining configuration with TypeScript support -- `loadConfig`: Loads configuration from file -- `calculateSizeDiff`: Calculates size differences between snapshots -- `renderMarkdownReport`: Generates markdown reports from size comparisons -- `fetchSnapshot`: Fetches size snapshots from S3 diff --git a/packages-internal/bundle-size-checker/bin/bundle-size-checker.js b/packages-internal/bundle-size-checker/bin/bundle-size-checker.js deleted file mode 100755 index a1b907fb44aa10..00000000000000 --- a/packages-internal/bundle-size-checker/bin/bundle-size-checker.js +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env node - -import '../src/create.js'; diff --git a/packages-internal/bundle-size-checker/package.json b/packages-internal/bundle-size-checker/package.json deleted file mode 100644 index 10b9cae5815ffc..00000000000000 --- a/packages-internal/bundle-size-checker/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "@mui/internal-bundle-size-checker", - "version": "1.0.0", - "private": true, - "description": "Bundle size checker for MUI packages", - "type": "module", - "main": "./src/index.js", - "bin": { - "bundle-size-checker": "./bin/bundle-size-checker.js" - }, - "scripts": { - "typescript": "tsc -p tsconfig.json" - }, - "dependencies": { - "@aws-sdk/client-s3": "^3.515.0", - "@aws-sdk/credential-providers": "^3.787.0", - "compression-webpack-plugin": "^10.0.0", - "execa": "^7.2.0", - "fast-glob": "^3.3.2", - "fs-extra": "^11.2.0", - "piscina": "^4.2.1", - "terser-webpack-plugin": "^5.3.10", - "webpack": "^5.90.3", - "webpack-bundle-analyzer": "^4.10.1", - "yargs": "^17.7.2" - }, - "devDependencies": { - "@types/webpack": "^5.28.5", - "@types/webpack-bundle-analyzer": "^4.7.0", - "@types/yargs": "^17.0.33" - } -} diff --git a/packages-internal/bundle-size-checker/src/configLoader.js b/packages-internal/bundle-size-checker/src/configLoader.js deleted file mode 100644 index c126a0a5f45b8a..00000000000000 --- a/packages-internal/bundle-size-checker/src/configLoader.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Utility to load the bundle-size-checker configuration - */ - -import fs from 'fs'; -import path from 'path'; - -/** - * Attempts to load and parse a single config file - * @param {string} configPath - Path to the configuration file - * @returns {Promise} The parsed config or null if file doesn't exist - * @throws {Error} If the file exists but has invalid format - */ -async function loadConfigFile(configPath) { - try { - if (!fs.existsSync(configPath)) { - return null; - } - - // Dynamic import for ESM - const configUrl = new URL(`file://${configPath}`); - const configModule = await import(configUrl.href); - let config = configModule.default; - - // Handle configs that might be Promise-returning functions - if (config instanceof Promise) { - config = await config; - } else if (typeof config === 'function') { - config = await config(); - } - - if (!config.entrypoints || !Array.isArray(config.entrypoints)) { - throw new Error('Configuration must include an entrypoints array'); - } - - return config; - } catch (error) { - console.error(`Error loading config from ${configPath}:`, error); - throw error; // Re-throw to indicate failure - } -} - -/** - * Attempts to load the config file from the given directory - * @param {string} rootDir - The directory to search for the config file - * @returns {Promise} A promise that resolves to the config object - */ -export async function loadConfig(rootDir) { - const configPaths = [ - path.join(rootDir, 'bundle-size-checker.config.js'), - path.join(rootDir, 'bundle-size-checker.config.mjs'), - ]; - - for (const configPath of configPaths) { - // eslint-disable-next-line no-await-in-loop - const config = await loadConfigFile(configPath); - if (config) { - return config; - } - } - - // Error out if no config file exists - throw new Error( - 'No bundle-size-checker configuration file found. Please create a bundle-size-checker.config.js or bundle-size-checker.config.mjs file in your project root.', - ); -} diff --git a/packages-internal/bundle-size-checker/src/create.js b/packages-internal/bundle-size-checker/src/create.js deleted file mode 100644 index 571d2ed7907973..00000000000000 --- a/packages-internal/bundle-size-checker/src/create.js +++ /dev/null @@ -1,119 +0,0 @@ -// @ts-check - -import path from 'path'; -import os from 'os'; -import fse from 'fs-extra'; -import yargs from 'yargs'; -import Piscina from 'piscina'; -import { loadConfig } from './configLoader.js'; -import { uploadSnapshot } from './uploadSnapshot.js'; - -const MAX_CONCURRENCY = Math.min(8, os.cpus().length); - -const rootDir = process.cwd(); - -/** - * creates size snapshot for every bundle that built with webpack - * @param {CommandLineArgs} args - * @param {BundleSizeCheckerConfig} config - The loaded configuration - * @returns {Promise>} - */ -async function getWebpackSizes(args, config) { - const worker = new Piscina({ - filename: new URL('./worker.js', import.meta.url).href, - maxThreads: MAX_CONCURRENCY, - }); - // Clean and recreate the build directory - const buildDir = path.join(rootDir, 'build'); - await fse.emptyDir(buildDir); - - if ( - !config || - !config.entrypoints || - !Array.isArray(config.entrypoints) || - config.entrypoints.length === 0 - ) { - throw new Error( - 'No valid configuration found. Create a bundle-size-checker.config.js or bundle-size-checker.config.mjs file with entrypoints array.', - ); - } - - const entries = config.entrypoints; - const uniqueEntries = new Set(entries); - - const sizeArrays = await Promise.all( - Array.from(uniqueEntries, (entry, index) => - worker.run({ entry, args, index, total: uniqueEntries.size }), - ), - ); - - return sizeArrays.flat(); -} - -/** - * Main runner function - * @param {CommandLineArgs} argv - Command line arguments - */ -async function run(argv) { - const { analyze, accurateBundles, output } = argv; - - const snapshotDestPath = output ? path.resolve(output) : path.join(rootDir, 'size-snapshot.json'); - - const config = await loadConfig(rootDir); - - const webpackSizes = await getWebpackSizes({ analyze, accurateBundles }, config); - const bundleSizes = Object.fromEntries(webpackSizes.sort((a, b) => a[0].localeCompare(b[0]))); - - // Ensure output directory exists - await fse.mkdirp(path.dirname(snapshotDestPath)); - await fse.writeJSON(snapshotDestPath, bundleSizes, { spaces: 2 }); - - // eslint-disable-next-line no-console - console.log(`Bundle size snapshot written to ${snapshotDestPath}`); - - // Upload the snapshot if upload configuration is provided - if (config && config.upload) { - try { - // eslint-disable-next-line no-console - console.log('Uploading bundle size snapshot to S3...'); - const { key } = await uploadSnapshot(snapshotDestPath, config.upload); - // eslint-disable-next-line no-console - console.log(`Bundle size snapshot uploaded to S3 with key: ${key}`); - } catch (/** @type {any} */ error) { - console.error('Failed to upload bundle size snapshot:', error.message); - // Exit with error code to indicate failure - process.exit(1); - } - } -} - -yargs(process.argv.slice(2)) - // @ts-expect-error - .command({ - command: '$0', - describe: 'Saves a size snapshot in size-snapshot.json', - builder: (cmdYargs) => { - return cmdYargs - .option('analyze', { - default: false, - describe: 'Creates a webpack-bundle-analyzer report for each bundle.', - type: 'boolean', - }) - .option('accurateBundles', { - default: false, - describe: 'Displays used bundles accurately at the cost of more CPU cycles.', - type: 'boolean', - }) - .option('output', { - alias: 'o', - describe: - 'Path to output the size snapshot JSON file (defaults to size-snapshot.json in current directory).', - type: 'string', - }); - }, - handler: run, - }) - .help() - .strict(true) - .version(false) - .parse(); diff --git a/packages-internal/bundle-size-checker/src/defineConfig.js b/packages-internal/bundle-size-checker/src/defineConfig.js deleted file mode 100644 index 001448c864d760..00000000000000 --- a/packages-internal/bundle-size-checker/src/defineConfig.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @typedef {Object} BundleSizeCheckerConfig - * @property {string[]} entrypoints - Array of entrypoints to check size for - */ - -/** - * Define a configuration for the bundle size checker. - * This is just a pass-through function for better TypeScript typing. - * - * @param {BundleSizeCheckerConfig} config - Configuration object - * @returns {BundleSizeCheckerConfig} The configuration object - */ -export default function defineConfig(config) { - return config; -} diff --git a/packages-internal/bundle-size-checker/src/fetchSnapshot.js b/packages-internal/bundle-size-checker/src/fetchSnapshot.js deleted file mode 100644 index 6442df49327f53..00000000000000 --- a/packages-internal/bundle-size-checker/src/fetchSnapshot.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * - * @param {string} repo - The name of the repository e.g. 'mui/material-ui' - * @param {string} sha - The commit SHA - * @returns {Promise} - The size snapshot data - */ -export async function fetchSnapshot(repo, sha) { - const url = `https://s3.eu-central-1.amazonaws.com/mui-org-ci/artifacts/${repo}/${sha}/size-snapshot.json`; - const response = await fetch(url); - if (!response.ok) { - if (repo === 'mui/material-ui') { - const legacyUrl = `https://s3.eu-central-1.amazonaws.com/mui-org-ci/artifacts/master/${sha}/size-snapshot.json`; - const legacyResponse = await fetch(legacyUrl); - if (legacyResponse.ok) { - return legacyResponse.json(); - } - } - throw new Error(`Failed to fetch "${url}", HTTP ${response.status}`); - } - return response.json(); -} diff --git a/packages-internal/bundle-size-checker/src/index.js b/packages-internal/bundle-size-checker/src/index.js deleted file mode 100644 index 94d7e5184e5f95..00000000000000 --- a/packages-internal/bundle-size-checker/src/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import defineConfig from './defineConfig.js'; -import { loadConfig } from './configLoader.js'; -import { calculateSizeDiff } from './sizeDiff.js'; -import { renderMarkdownReport } from './renderMarkdownReport.js'; -import { fetchSnapshot } from './fetchSnapshot.js'; - -export { defineConfig, loadConfig, calculateSizeDiff, renderMarkdownReport, fetchSnapshot }; diff --git a/packages-internal/bundle-size-checker/src/renderMarkdownReport.js b/packages-internal/bundle-size-checker/src/renderMarkdownReport.js deleted file mode 100644 index 03d6b436341f86..00000000000000 --- a/packages-internal/bundle-size-checker/src/renderMarkdownReport.js +++ /dev/null @@ -1,232 +0,0 @@ -/** - * @typedef {import('./sizeDiff.js').Size} Size - * @typedef {import('./sizeDiff.js').SizeSnapshot} SizeSnapshot - * @typedef {import('./sizeDiff.js').ComparisonResult} ComparisonResult - */ - -import { calculateSizeDiff } from './sizeDiff.js'; -import { fetchSnapshot } from './fetchSnapshot.js'; - -const displayPercentFormatter = new Intl.NumberFormat(undefined, { - style: 'percent', - signDisplay: 'exceptZero', - minimumFractionDigits: 2, - maximumFractionDigits: 2, - useGrouping: true, -}); - -// Formatter for byte sizes (absolute values) - no sign -const byteSizeFormatter = new Intl.NumberFormat(undefined, { - style: 'unit', - unit: 'byte', - notation: 'compact', - unitDisplay: 'narrow', - maximumSignificantDigits: 3, - minimumSignificantDigits: 1, -}); - -// Formatter for size changes - always show sign -// Created by extending the options from byteSizeFormatter -const byteSizeChangeFormatter = new Intl.NumberFormat(undefined, { - ...byteSizeFormatter.resolvedOptions(), - signDisplay: 'exceptZero', -}); - -/** - * - * @param {'▲' | '▼'} symbol - * @param {'yellow'|'red'|'blue'|'green'} color - * @returns - */ -function formatSymbol(symbol, color) { - return `\${\\tiny{\\color{${color}}${symbol}}}$`; -} - -/** - * Generates a symbol based on the relative change value. - * @param {number|null} relative - The relative change as a Number - * @returns {string} Formatted size change string with symbol - */ -function getChangeIcon(relative) { - if (relative === null) { - return formatSymbol('▲', 'yellow'); - } - if (relative === -1) { - return formatSymbol('▼', 'blue'); - } - if (relative < 0) { - return formatSymbol('▼', 'green'); - } - if (relative > 0) { - return formatSymbol('▲', 'red'); - } - return ' '; -} - -/** - * Formats the relative change value for display. - * @param {number|null} value - The relative change as a Number - * @returns {string} Formatted relative change string - */ -function formatRelativeChange(value) { - if (value === null) { - return 'new'; - } - if (value === -1) { - return 'removed'; - } - return displayPercentFormatter.format(value); -} - -/** - * Generates a user-readable string from a percentage change. - * @param {number} absolute - The absolute change as a Number - * @param {number|null} relative - The relative change as a Number - * @returns {string} Formatted percentage string with emoji - */ -function formatChange(absolute, relative) { - const formattedAbsolute = byteSizeChangeFormatter.format(absolute); - const formattedChange = formatRelativeChange(relative); - return `${getChangeIcon(relative)}${formattedAbsolute}(${formattedChange})`; -} - -/** - * Generates emphasized change text for a single bundle - * @param {Size} entry - Bundle entry - * @returns {string} Formatted change text - */ -function generateEmphasizedChange({ id: bundle, parsed, gzip }) { - // increase might be a bug fix which is a nice thing. reductions are always nice - const changeParsed = formatChange(parsed.absoluteDiff, parsed.relativeDiff); - const changeGzip = formatChange(gzip.absoluteDiff, gzip.relativeDiff); - - return `**${bundle}** **parsed:**${changeParsed} **gzip:**${changeGzip}`; -} - -/** - * Generates a Markdown report for bundle size changes - * @param {ComparisonResult} comparison - Comparison result from calculateSizeDiff - * @param {Object} [options] - Additional options - * @param {number} [options.visibleLimit=10] - Number of entries to show before collapsing - * @param {number} [options.parsedSizeChangeThreshold=300] - Threshold for parsed size change by which to show the entry - * @param {number} [options.gzipSizeChangeThreshold=100] - Threshold for gzipped size change by which to show the entry - * @returns {string} Markdown report - */ -function renderMarkdownReportContent( - comparison, - { visibleLimit = 10, parsedSizeChangeThreshold = 300, gzipSizeChangeThreshold = 100 } = {}, -) { - let markdownContent = ''; - - markdownContent += `**Total Size Change:**${formatChange( - comparison.totals.totalParsed, - comparison.totals.totalParsedPercent, - )} - **Total Gzip Change:**${formatChange( - comparison.totals.totalGzip, - comparison.totals.totalGzipPercent, - )}\n`; - - markdownContent += `Files: ${comparison.fileCounts.total} total (${ - comparison.fileCounts.added - } added, ${comparison.fileCounts.removed} removed, ${comparison.fileCounts.changed} changed)\n\n`; - - const changedEntries = comparison.entries.filter( - (entry) => Math.abs(entry.parsed.absoluteDiff) > 0 || Math.abs(entry.gzip.absoluteDiff) > 0, - ); - - const visibleEntries = []; - const hiddenEntries = []; - - for (const entry of changedEntries) { - const { parsed, gzip } = entry; - const isSignificantChange = - Math.abs(parsed.absoluteDiff) > parsedSizeChangeThreshold || - Math.abs(gzip.absoluteDiff) > gzipSizeChangeThreshold; - if (isSignificantChange && visibleEntries.length < visibleLimit) { - visibleEntries.push(entry); - } else { - hiddenEntries.push(entry); - } - } - - const importantChanges = visibleEntries.map(generateEmphasizedChange); - const hiddenChanges = hiddenEntries.map(generateEmphasizedChange); - - // Add important changes to markdown - if (importantChanges.length > 0) { - // Show the most significant changes first, up to the visible limit - const visibleChanges = importantChanges.slice(0, visibleLimit); - markdownContent += `${visibleChanges.join('\n')}`; - } - - // If there are more changes, add them in a collapsible details section - if (hiddenChanges.length > 0) { - markdownContent += `\n
\nShow ${hiddenChanges.length} more bundle changes\n\n`; - markdownContent += `${hiddenChanges.join('\n')}\n\n`; - markdownContent += `
`; - } - - return markdownContent; -} - -/** - * - * @param {Object} prInfo - * @param {number} prInfo.number - The pull request number - * @param {Object} prInfo.base - The base branch of the pull request - * @param {string} prInfo.base.ref - The base branch name - * @param {string} prInfo.base.sha - The base branch SHA - * @param {string} [circleciBuildNumber] - The CircleCI build number - * @returns {URL} - */ -function getDetailsUrl(prInfo, circleciBuildNumber) { - const detailedComparisonUrl = new URL('https://mui-dashboard.netlify.app/size-comparison'); - detailedComparisonUrl.searchParams.set('baseRef', prInfo.base.ref); - detailedComparisonUrl.searchParams.set('baseCommit', prInfo.base.sha); - detailedComparisonUrl.searchParams.set('prNumber', String(prInfo.number)); - if (circleciBuildNumber) { - detailedComparisonUrl.searchParams.set('circleCIBuildNumber', circleciBuildNumber); - } - return detailedComparisonUrl; -} - -/** - * - * @param {Object} prInfo - * @param {number} prInfo.number - The pull request number - * @param {Object} prInfo.base - The base branch of the pull request - * @param {string} prInfo.base.ref - The base branch name - * @param {string} prInfo.base.sha - The base branch SHA - * @param {Object} prInfo.head - The head branch of the pull request - * @param {string} prInfo.head.ref - The head branch name - * @param {string} prInfo.head.sha - The head branch SHA - * @param {string} [circleciBuildNumber] - The CircleCI build number - * @returns - */ -export async function renderMarkdownReport(prInfo, circleciBuildNumber) { - let markdownContent = ''; - - const baseCommit = prInfo.base.sha; - const prCommit = prInfo.head.sha; - const [baseSnapshot, prSnapshot] = await Promise.all([ - fetchSnapshot('mui/material-ui', baseCommit).catch((error) => { - console.error(`Error fetching base snapshot: ${error}`); - return null; - }), - fetchSnapshot('mui/material-ui', prCommit), - ]); - - if (!baseSnapshot) { - markdownContent += `_:no_entry_sign: No bundle size snapshot found for base commit ${baseCommit}._\n\n`; - } - - const sizeDiff = calculateSizeDiff(baseSnapshot ?? {}, prSnapshot); - - const report = await renderMarkdownReportContent(sizeDiff); - - markdownContent += report; - - markdownContent += `\n\n[Details of bundle changes](${getDetailsUrl(prInfo, circleciBuildNumber)})`; - - return markdownContent; -} diff --git a/packages-internal/bundle-size-checker/src/sizeDiff.js b/packages-internal/bundle-size-checker/src/sizeDiff.js deleted file mode 100644 index 2fa81481d5cd34..00000000000000 --- a/packages-internal/bundle-size-checker/src/sizeDiff.js +++ /dev/null @@ -1,199 +0,0 @@ -/** - * @description Represents a single bundle size entry - * @typedef {Object} SizeSnapshotEntry - * @property {number} parsed - * @property {number} gzip - * - * @description Represents a single bundle size snapshot - * @typedef {Object.} SizeSnapshot - * - * @description Represents a single bundle size comparison - * @typedef {Object} Size - * @property {string} id - Bundle identifier - * @property {Object} parsed - Parsed size information - * @property {number} parsed.previous - Previous parsed size - * @property {number} parsed.current - Current parsed size - * @property {number} parsed.absoluteDiff - Absolute difference in parsed size - * @property {number|null} parsed.relativeDiff - Relative difference in parsed size - * @property {Object} gzip - Gzipped size information - * @property {number} gzip.previous - Previous gzipped size - * @property {number} gzip.current - Current gzipped size - * @property {number} gzip.absoluteDiff - Absolute difference in gzipped size - * @property {number|null} gzip.relativeDiff - Relative difference in gzipped size - * - * @description Represents the comparison results - * @typedef {Object} ComparisonResult - * @property {Size[]} entries - Size entries for each bundle - * @property {Object} totals - Total size information - * @property {number} totals.totalParsed - Total parsed size difference - * @property {number} totals.totalGzip - Total gzipped size difference - * @property {number} totals.totalParsedPercent - Total parsed size percentage difference - * @property {number} totals.totalGzipPercent - Total gzipped size percentage difference - * @property {Object} fileCounts - File count information - * @property {number} fileCounts.added - Number of added files - * @property {number} fileCounts.removed - Number of removed files - * @property {number} fileCounts.changed - Number of changed files - * @property {number} fileCounts.total - Total number of files - */ - -const nullSnapshot = { parsed: 0, gzip: 0 }; - -/** - * Calculates size difference between two snapshots - * - * @param {SizeSnapshot} baseSnapshot - Base snapshot (previous) - * @param {SizeSnapshot} targetSnapshot - Target snapshot (current) - * @returns {ComparisonResult} Comparison result with entries, totals, and file counts - */ -export function calculateSizeDiff(baseSnapshot, targetSnapshot) { - const bundleKeys = Object.keys({ ...baseSnapshot, ...targetSnapshot }); - /** @type {Size[]} */ - const results = []; - - // Track totals - let totalParsed = 0; - let totalGzip = 0; - let totalParsedPrevious = 0; - let totalGzipPrevious = 0; - - // Track file counts - let addedFiles = 0; - let removedFiles = 0; - let changedFiles = 0; - - bundleKeys.forEach((bundle) => { - const isNewBundle = !baseSnapshot[bundle]; - const isRemovedBundle = !targetSnapshot[bundle]; - const currentSize = targetSnapshot[bundle] || nullSnapshot; - const previousSize = baseSnapshot[bundle] || nullSnapshot; - - // Update file counts - if (isNewBundle) { - addedFiles += 1; - } else if (isRemovedBundle) { - removedFiles += 1; - } else if ( - currentSize.parsed !== previousSize.parsed || - currentSize.gzip !== previousSize.gzip - ) { - changedFiles += 1; - } - - const parsedDiff = currentSize.parsed - previousSize.parsed; - const gzipDiff = currentSize.gzip - previousSize.gzip; - - // Calculate relative diffs with appropriate handling of new/removed bundles - let parsedRelativeDiff; - if (isNewBundle) { - parsedRelativeDiff = null; - } else if (isRemovedBundle) { - parsedRelativeDiff = -1; - } else if (previousSize.parsed) { - parsedRelativeDiff = currentSize.parsed / previousSize.parsed - 1; - } else { - parsedRelativeDiff = 0; - } - - let gzipRelativeDiff; - if (isNewBundle) { - gzipRelativeDiff = null; - } else if (isRemovedBundle) { - gzipRelativeDiff = -1; - } else if (previousSize.gzip) { - gzipRelativeDiff = currentSize.gzip / previousSize.gzip - 1; - } else { - gzipRelativeDiff = 0; - } - - const entry = { - id: bundle, - parsed: { - previous: previousSize.parsed, - current: currentSize.parsed, - absoluteDiff: parsedDiff, - relativeDiff: parsedRelativeDiff, - }, - gzip: { - previous: previousSize.gzip, - current: currentSize.gzip, - absoluteDiff: gzipDiff, - relativeDiff: gzipRelativeDiff, - }, - }; - - results.push(entry); - - // Update totals - totalParsed += parsedDiff; - totalGzip += gzipDiff; - totalParsedPrevious += previousSize.parsed; - totalGzipPrevious += previousSize.gzip; - }); - - // Calculate percentage changes - const totalParsedPercent = totalParsedPrevious > 0 ? totalParsed / totalParsedPrevious : 0; - const totalGzipPercent = totalGzipPrevious > 0 ? totalGzip / totalGzipPrevious : 0; - - // Sort the results - // Custom sorting: - // 1. Existing bundles that increased in size (larger increases first) - // 2. New bundles (larger sizes first) - // 3. Existing bundles that decreased in size (larger decreases first) - // 4. Removed bundles (larger sizes first) - // 5. Unchanged bundles (alphabetically) - results.sort((entryA, entryB) => { - // Helper function to determine bundle category (for sorting) - /** @type {(entry: Size) => number} */ - const getCategory = (entry) => { - if (entry.parsed.relativeDiff === null) { - return 2; // New bundle - } - if (entry.parsed.relativeDiff === -1) { - return 4; // Removed bundle - } - if (entry.parsed.relativeDiff > 0) { - return 1; // Increased - } - if (entry.parsed.relativeDiff < 0) { - return 3; // Decreased - } - return 5; // Unchanged - }; - - // Get categories for both bundles - const categoryA = getCategory(entryA); - const categoryB = getCategory(entryB); - - // Sort by category first - if (categoryA !== categoryB) { - return categoryA - categoryB; - } - - // Within the same category, sort by absolute diff (largest first) - const diffA = Math.abs(entryA.parsed.absoluteDiff); - const diffB = Math.abs(entryB.parsed.absoluteDiff); - - if (diffA !== diffB) { - return diffB - diffA; - } - - // If diffs are the same, sort by name - return entryA.id.localeCompare(entryB.id); - }); - - return { - entries: results, - totals: { - totalParsed, - totalGzip, - totalParsedPercent, - totalGzipPercent, - }, - fileCounts: { - added: addedFiles, - removed: removedFiles, - changed: changedFiles, - total: bundleKeys.length, - }, - }; -} diff --git a/packages-internal/bundle-size-checker/src/types.d.ts b/packages-internal/bundle-size-checker/src/types.d.ts deleted file mode 100644 index 04f35fbf95bc5b..00000000000000 --- a/packages-internal/bundle-size-checker/src/types.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -// WebpackEntry type -interface WebpackEntry { - import: string; - importName?: string; -} - -// Webpack stats types -interface StatsAsset { - name: string; - size: number; - related?: { - find: (predicate: (asset: any) => boolean) => { size: number; type: string }; - }; -} - -interface StatsChunkGroup { - name: string; - assets: Array<{ name: string; size: number }>; -} - -interface WebpackStats { - hasErrors(): boolean; - toJson(options: any): { - assets?: StatsAsset[]; - entrypoints?: Record; - errors?: any[]; - }; -} - -// Upload configuration -interface UploadConfig { - project: string; // The project name (e.g., "mui/material-ui") - branch?: string; // Optional branch name (defaults to current Git branch) - isPullRequest?: boolean; // Whether this is a pull request build (defaults to false) -} - -// Bundle size checker config -interface BundleSizeCheckerConfig { - entrypoints: string[]; - upload?: UploadConfig; -} - -// Command line argument types -interface CommandLineArgs { - analyze?: boolean; - accurateBundles?: boolean; - output?: string; -} diff --git a/packages-internal/bundle-size-checker/src/uploadSnapshot.js b/packages-internal/bundle-size-checker/src/uploadSnapshot.js deleted file mode 100644 index cedb29cc5a00e6..00000000000000 --- a/packages-internal/bundle-size-checker/src/uploadSnapshot.js +++ /dev/null @@ -1,89 +0,0 @@ -import fs from 'fs'; -import { S3Client, PutObjectCommand, PutObjectTaggingCommand } from '@aws-sdk/client-s3'; -import { execa } from 'execa'; -import { fromEnv } from '@aws-sdk/credential-providers'; - -/** - * Gets the current Git branch name - * @returns {Promise} The current branch name - */ -async function getCurrentBranch() { - try { - const { stdout } = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD']); - return stdout.trim(); - } catch (/** @type {any} */ error) { - console.warn('Failed to determine Git branch:', error); - return 'unknown-branch'; - } -} - -/** - * Gets the current Git commit SHA - * @returns {Promise} The current commit SHA - */ -async function getCurrentCommitSHA() { - const { stdout } = await execa('git', ['rev-parse', 'HEAD']); - return stdout.trim(); -} - -/** - * Uploads the size snapshot to S3 - * @param {string} snapshotPath - The path to the size snapshot JSON file - * @param {UploadConfig} uploadConfig - The upload configuration - * @param {string} [commitSha] - Optional commit SHA (defaults to current Git HEAD) - * @returns {Promise<{key:string}>} - */ -export async function uploadSnapshot(snapshotPath, uploadConfig, commitSha) { - if (!uploadConfig || !uploadConfig.project) { - throw new Error('Upload configuration is missing or invalid'); - } - - // Run git operations and file reading in parallel - const [sha, branch, fileContent] = await Promise.all([ - // Get the current commit SHA if not provided - commitSha || getCurrentCommitSHA(), - // Get branch name if not provided - uploadConfig.branch || getCurrentBranch(), - // Read the snapshot file - fs.promises.readFile(snapshotPath), - ]); - - // Default isPullRequest is false - const isPullRequest = uploadConfig.isPullRequest || false; - - // Create S3 client (uses AWS credentials from environment) - const client = new S3Client({ - region: process.env.AWS_REGION_ARTIFACTS || process.env.AWS_REGION || 'eu-central-1', - credentials: fromEnv(), - }); - - // S3 bucket and key - const bucket = 'mui-org-ci'; - const key = `artifacts/${uploadConfig.project}/${sha}/size-snapshot.json`; - - // Upload the file first - await client.send( - new PutObjectCommand({ - Bucket: bucket, - Key: key, - Body: fileContent, - ContentType: 'application/json', - }), - ); - - // Then add tags to the uploaded object - await client.send( - new PutObjectTaggingCommand({ - Bucket: bucket, - Key: key, - Tagging: { - TagSet: [ - { Key: 'isPullRequest', Value: isPullRequest ? 'yes' : 'no' }, - { Key: 'branch', Value: branch }, - ], - }, - }), - ); - - return { key }; -} diff --git a/packages-internal/bundle-size-checker/src/worker.js b/packages-internal/bundle-size-checker/src/worker.js deleted file mode 100644 index 991bd221be1403..00000000000000 --- a/packages-internal/bundle-size-checker/src/worker.js +++ /dev/null @@ -1,183 +0,0 @@ -import { promisify } from 'util'; -import path from 'path'; -import webpackCallbackBased from 'webpack'; -import CompressionPlugin from 'compression-webpack-plugin'; -import TerserPlugin from 'terser-webpack-plugin'; -import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; - -/** - * @type {(options: webpackCallbackBased.Configuration) => Promise} - */ -// @ts-expect-error Can't select the right overload -const webpack = promisify(webpackCallbackBased); -const rootDir = process.cwd(); - -// Type declarations are now in types.d.ts - -/** - * Creates webpack configuration for bundle size checking - * @param {string} entryName - Entry point string - * @param {CommandLineArgs} args - * @returns {import('webpack').Configuration} - */ -function createWebpackConfig(entryName, args) { - const analyzerMode = args.analyze ? 'static' : 'disabled'; - const concatenateModules = !args.accurateBundles; - - const [importSrc, importName] = entryName.split('#'); - - const entryContent = importName - ? `import { ${importName} as foo } from '${importSrc}';console.log(foo);` - : `import * as foo from '${importSrc}';console.log(foo);`; - - /** - * @type {import('webpack').Configuration} - */ - const configuration = { - // ideally this would be computed from the bundles peer dependencies - // Ensure that `react` as well as `react/*` are considered externals but not `react*` - externals: /^(date-fns|dayjs|luxon|moment|react|react-dom)(\/.*)?$/, - mode: 'production', - optimization: { - concatenateModules, - minimizer: [ - new TerserPlugin({ - test: /\.m?js(\?.*)?$/i, - // Avoid creating LICENSE.txt files for each module - // See https://github.com/webpack-contrib/terser-webpack-plugin#remove-comments - terserOptions: { - format: { - comments: false, - }, - }, - extractComments: false, - }), - ], - }, - output: { - filename: '[name].js', - library: { - // TODO: Use `type: 'module'` once it is supported (currently incompatible with `externals`) - name: 'M', - type: 'var', - // type: 'module', - }, - path: path.join(rootDir, 'build'), - }, - plugins: [ - new CompressionPlugin({ - filename: '[path][base][fragment].gz', - }), - new BundleAnalyzerPlugin({ - analyzerMode, - // We create a report for each bundle so around 120 reports. - // Opening them all is spam. - // If opened with `webpack --config . --analyze` it'll still open one new tab though. - openAnalyzer: false, - // '[name].html' not supported: https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/12 - reportFilename: `${importSrc}.html`, - }), - ], - // A context to the current dir, which has a node_modules folder with workspace dependencies - context: rootDir, - entry: { - // This format is a data: url combined with inline matchResource to obtain a virtual entry. - // See https://github.com/webpack/webpack/issues/6437#issuecomment-874466638 - // See https://webpack.js.org/api/loaders/#inline-matchresource - [entryName]: `./index.js!=!data:text/javascript,${entryContent}`, - }, - // TODO: 'browserslist:modern' - // See https://github.com/webpack/webpack/issues/14203 - target: 'web', - }; - - return configuration; -} - -/** - * Get sizes for a bundle - * @param {{ entry: string, args: CommandLineArgs, index: number, total: number }} options - * @returns {Promise>} - */ -export default async function getSizes({ entry, args, index, total }) { - /** @type {Array<[string, { parsed: number, gzip: number }]>} */ - const sizes = []; - - const configuration = createWebpackConfig(entry, args); - - // eslint-disable-next-line no-console -- process monitoring - console.log(`Compiling ${index + 1}/${total}: "${entry}"`); - - const webpackStats = await webpack(configuration); - - if (!webpackStats) { - throw new Error('No webpack stats were returned'); - } - - if (webpackStats.hasErrors()) { - const statsJson = webpackStats.toJson({ - all: false, - entrypoints: true, - errors: true, - }); - - const entrypointKeys = statsJson.entrypoints ? Object.keys(statsJson.entrypoints) : []; - - throw new Error( - `The following errors occurred during bundling of ${entrypointKeys.join(', ')} with webpack: \n${( - statsJson.errors || [] - ) - .map((error) => { - return `${JSON.stringify(error, null, 2)}`; - }) - .join('\n')}`, - ); - } - - const stats = webpackStats.toJson({ - all: false, - assets: true, - entrypoints: true, - relatedAssets: true, - }); - - if (!stats.assets) { - return sizes; - } - - const assets = new Map(stats.assets.map((asset) => [asset.name, asset])); - - if (stats.entrypoints) { - Object.values(stats.entrypoints).forEach((entrypoint) => { - let parsedSize = 0; - let gzipSize = 0; - - if (entrypoint.assets) { - entrypoint.assets.forEach(({ name, size }) => { - const asset = assets.get(name); - if (asset && asset.related) { - const gzippedAsset = asset.related.find((relatedAsset) => { - return relatedAsset.type === 'gzipped'; - }); - - if (size !== undefined) { - parsedSize += size; - } - - if (gzippedAsset && gzippedAsset.size !== undefined) { - gzipSize += gzippedAsset.size; - } - } - }); - } - - if (!entrypoint.name) { - throw new Error('Entrypoint name is undefined'); - } - - sizes.push([entrypoint.name, { parsed: parsedSize, gzip: gzipSize }]); - }); - } - - return sizes; -} diff --git a/packages-internal/bundle-size-checker/tsconfig.json b/packages-internal/bundle-size-checker/tsconfig.json deleted file mode 100644 index c0ef407b7f6c80..00000000000000 --- a/packages-internal/bundle-size-checker/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "target": "ES2020", - "module": "ESNext", - "moduleResolution": "node", - "allowJs": true, - "checkJs": true, - "skipLibCheck": true, - "strict": true, - "esModuleInterop": true, - "resolveJsonModule": true, - "isolatedModules": true, - "outDir": "./build", - "noEmit": true - }, - "include": ["**/*.js", "**/*.mjs", "**/*.ts"], - "exclude": ["node_modules", "build"] -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3983187e83b7bc..bddd11a032a4b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,8 +34,8 @@ importers: specifier: ^9.6.0 version: 9.6.0(encoding@0.1.13) '@mui/internal-bundle-size-checker': - specifier: workspace:* - version: link:packages-internal/bundle-size-checker + specifier: github:mui/mui-public#bundle-size-checker&path:./packages/bundle-size-checker + version: https://codeload.github.com/mui/mui-public/tar.gz/4d551b5e95b5ec09624f8ee71fc68dd7b653d581#path:./packages/bundle-size-checker(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) '@netlify/functions': specifier: ^3.0.4 version: 3.0.4 @@ -641,8 +641,8 @@ importers: bundle-size: dependencies: '@mui/internal-bundle-size-checker': - specifier: workspace:* - version: link:../packages-internal/bundle-size-checker + specifier: github:mui/mui-public#bundle-size-checker&path:./packages/bundle-size-checker + version: https://codeload.github.com/mui/mui-public/tar.gz/4d551b5e95b5ec09624f8ee71fc68dd7b653d581#path:./packages/bundle-size-checker(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) '@mui/lab': specifier: workspace:* version: link:../packages/mui-lab/build @@ -1070,52 +1070,6 @@ importers: specifier: ^1.20.6 version: 1.20.6 - packages-internal/bundle-size-checker: - dependencies: - '@aws-sdk/client-s3': - specifier: ^3.515.0 - version: 3.787.0 - '@aws-sdk/credential-providers': - specifier: ^3.787.0 - version: 3.787.0 - compression-webpack-plugin: - specifier: ^10.0.0 - version: 10.0.0(webpack@5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))) - execa: - specifier: ^7.2.0 - version: 7.2.0 - fast-glob: - specifier: ^3.3.2 - version: 3.3.3 - fs-extra: - specifier: ^11.2.0 - version: 11.3.0 - piscina: - specifier: ^4.2.1 - version: 4.9.2 - terser-webpack-plugin: - specifier: ^5.3.10 - version: 5.3.14(webpack@5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))) - webpack: - specifier: ^5.90.3 - version: 5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) - webpack-bundle-analyzer: - specifier: ^4.10.1 - version: 4.10.2 - yargs: - specifier: ^17.7.2 - version: 17.7.2 - devDependencies: - '@types/webpack': - specifier: ^5.28.5 - version: 5.28.5(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) - '@types/webpack-bundle-analyzer': - specifier: ^4.7.0 - version: 4.7.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) - '@types/yargs': - specifier: ^17.0.33 - version: 17.0.33 - packages-internal/docs-utils: dependencies: rimraf: @@ -4453,6 +4407,11 @@ packages: '@mui/core-downloads-tracker@5.15.14': resolution: {integrity: sha512-on75VMd0XqZfaQW+9pGjSNiqW+ghc5E2ZSLRBXwcXl/C4YzjfyjrLPhrEpKnR9Uym9KXBvxrhoHfPcczYHweyA==} + '@mui/internal-bundle-size-checker@https://codeload.github.com/mui/mui-public/tar.gz/4d551b5e95b5ec09624f8ee71fc68dd7b653d581#path:./packages/bundle-size-checker': + resolution: {path: ./packages/bundle-size-checker, tarball: https://codeload.github.com/mui/mui-public/tar.gz/4d551b5e95b5ec09624f8ee71fc68dd7b653d581} + version: 1.0.0 + hasBin: true + '@mui/joy@5.0.0-beta.22': resolution: {integrity: sha512-XFJd/cWXqt9MMlaUh10QQH893YaRw2CORYRhQovXvaJk7mmt/Sc4q3Fb7ANCXf4xMUPdwqdnvawLkAOAKVHuXg==} engines: {node: '>=12.0.0'} @@ -6431,12 +6390,6 @@ packages: '@types/webfontloader@1.6.38': resolution: {integrity: sha512-kUaF72Fv202suFx6yBrwXqeVRMx7hGtJTesyESZgn9sEPCUeDXm2p0SiyS1MTqW74nQP4p7JyrOCwZ7pNFns4w==} - '@types/webpack-bundle-analyzer@4.7.0': - resolution: {integrity: sha512-c5i2ThslSNSG8W891BRvOd/RoCjI2zwph8maD22b1adtSns20j+0azDDMCK06DiVrzTgnwiDl5Ntmu1YRJw8Sg==} - - '@types/webpack@5.28.5': - resolution: {integrity: sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==} - '@types/webxr@0.5.21': resolution: {integrity: sha512-geZIAtLzjGmgY2JUi6VxXdCrTb99A7yP49lxLr2Nm/uIK0PkkxcEi4OGhoGDO4pxCf3JwGz2GiJL2Ej4K2bKaA==} @@ -16945,6 +16898,28 @@ snapshots: '@mui/core-downloads-tracker@5.15.14': {} + '@mui/internal-bundle-size-checker@https://codeload.github.com/mui/mui-public/tar.gz/4d551b5e95b5ec09624f8ee71fc68dd7b653d581#path:./packages/bundle-size-checker(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))': + dependencies: + '@aws-sdk/client-s3': 3.787.0 + '@aws-sdk/credential-providers': 3.787.0 + compression-webpack-plugin: 10.0.0(webpack@5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))) + execa: 7.2.0 + fast-glob: 3.3.3 + fs-extra: 11.3.0 + piscina: 4.9.2 + terser-webpack-plugin: 5.3.14(webpack@5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))) + webpack: 5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) + webpack-bundle-analyzer: 4.10.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@swc/core' + - aws-crt + - bufferutil + - esbuild + - uglify-js + - utf-8-validate + - webpack-cli + '@mui/joy@5.0.0-beta.22(@emotion/react@11.13.5(@types/react@19.1.2)(react@19.1.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react@19.1.0))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.27.0 @@ -19372,28 +19347,6 @@ snapshots: '@types/webfontloader@1.6.38': {} - '@types/webpack-bundle-analyzer@4.7.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))': - dependencies: - '@types/node': 20.17.30 - tapable: 2.2.1 - webpack: 5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - - webpack-cli - - '@types/webpack@5.28.5(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))': - dependencies: - '@types/node': 20.17.30 - tapable: 2.2.1 - webpack: 5.98.0(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - - webpack-cli - '@types/webxr@0.5.21': {} '@types/ws@8.5.13': From ffc3fd37df8fc26d260a9d95899c51dfdef97a9c Mon Sep 17 00:00:00 2001 From: MUI bot <2109932+Janpot@users.noreply.github.com> Date: Tue, 29 Apr 2025 14:41:21 +0200 Subject: [PATCH 18/18] reorg --- bundle-size/package.json | 10 ++++++---- package.json | 4 ++-- pnpm-lock.yaml | 15 ++++++++------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/bundle-size/package.json b/bundle-size/package.json index f204c2f700f5a2..c2089b16449615 100644 --- a/bundle-size/package.json +++ b/bundle-size/package.json @@ -6,14 +6,16 @@ "scripts": { "check": "NODE_OPTIONS=\"--max-old-space-size=4096\" bundle-size-checker --output ../size-snapshot.json" }, + "devDependencies": { + "@mui/bundle-size-checker": "github:mui/mui-public#bundle-size-checker&path:./packages/bundle-size-checker", + "fast-glob": "^3.3.2", + "path": "^0.12.7" + }, "dependencies": { - "@mui/internal-bundle-size-checker": "github:mui/mui-public#bundle-size-checker&path:./packages/bundle-size-checker", "@mui/material": "workspace:*", "@mui/lab": "workspace:*", "@mui/private-theming": "workspace:*", "@mui/system": "workspace:*", - "@mui/utils": "workspace:*", - "fast-glob": "^3.3.2", - "path": "^0.12.7" + "@mui/utils": "workspace:*" } } diff --git a/package.json b/package.json index a4b264c3d24082..9f1408a7a934fd 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,8 @@ "valelint": "git ls-files | grep -h \".md$\" | xargs vale --filter='.Level==\"error\"'", "prettier": "pretty-quick --ignore-path .eslintignore --branch master", "prettier:all": "prettier --write . --ignore-path .eslintignore", - "size:snapshot": "pnpm --filter @mui/bundle-size check", - "size:why": "pnpm --filter @mui/bundle-size run check -- --analyze", + "size:snapshot": "pnpm -F @mui/bundle-size check", + "size:why": "pnpm size:snapshot --analyze", "start": "pnpm install && pnpm docs:dev", "test": "node scripts/test.mjs", "tc": "nx run nx_test_tc", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec07aa187cd0af..627a86105e54ad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,7 +35,7 @@ importers: version: 9.6.0(encoding@0.1.13) '@mui/internal-bundle-size-checker': specifier: github:mui/mui-public#bundle-size-checker&path:./packages/bundle-size-checker - version: https://codeload.github.com/mui/mui-public/tar.gz/4d551b5e95b5ec09624f8ee71fc68dd7b653d581#path:./packages/bundle-size-checker(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) + version: https://codeload.github.com/mui/mui-public/tar.gz/e81ad6cfa78b3bca1c5de0195604f0544b1a8c2b#path:./packages/bundle-size-checker(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) '@netlify/functions': specifier: ^3.1.2 version: 3.1.2(encoding@0.1.13)(rollup@4.40.0) @@ -640,9 +640,6 @@ importers: bundle-size: dependencies: - '@mui/internal-bundle-size-checker': - specifier: github:mui/mui-public#bundle-size-checker&path:./packages/bundle-size-checker - version: https://codeload.github.com/mui/mui-public/tar.gz/4d551b5e95b5ec09624f8ee71fc68dd7b653d581#path:./packages/bundle-size-checker(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0)) '@mui/lab': specifier: workspace:* version: link:../packages/mui-lab/build @@ -658,6 +655,10 @@ importers: '@mui/utils': specifier: workspace:* version: link:../packages/mui-utils/build + devDependencies: + '@mui/bundle-size-checker': + specifier: github:mui/mui-public#bundle-size-checker&path:./packages/bundle-size-checker + version: '@mui/internal-bundle-size-checker@https://codeload.github.com/mui/mui-public/tar.gz/e81ad6cfa78b3bca1c5de0195604f0544b1a8c2b#path:./packages/bundle-size-checker(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))' fast-glob: specifier: ^3.3.2 version: 3.3.3 @@ -4561,8 +4562,8 @@ packages: '@mui/core-downloads-tracker@5.15.14': resolution: {integrity: sha512-on75VMd0XqZfaQW+9pGjSNiqW+ghc5E2ZSLRBXwcXl/C4YzjfyjrLPhrEpKnR9Uym9KXBvxrhoHfPcczYHweyA==} - '@mui/internal-bundle-size-checker@https://codeload.github.com/mui/mui-public/tar.gz/4d551b5e95b5ec09624f8ee71fc68dd7b653d581#path:./packages/bundle-size-checker': - resolution: {path: ./packages/bundle-size-checker, tarball: https://codeload.github.com/mui/mui-public/tar.gz/4d551b5e95b5ec09624f8ee71fc68dd7b653d581} + '@mui/internal-bundle-size-checker@https://codeload.github.com/mui/mui-public/tar.gz/e81ad6cfa78b3bca1c5de0195604f0544b1a8c2b#path:./packages/bundle-size-checker': + resolution: {path: ./packages/bundle-size-checker, tarball: https://codeload.github.com/mui/mui-public/tar.gz/e81ad6cfa78b3bca1c5de0195604f0544b1a8c2b} version: 1.0.0 hasBin: true @@ -17646,7 +17647,7 @@ snapshots: '@mui/core-downloads-tracker@5.15.14': {} - '@mui/internal-bundle-size-checker@https://codeload.github.com/mui/mui-public/tar.gz/4d551b5e95b5ec09624f8ee71fc68dd7b653d581#path:./packages/bundle-size-checker(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))': + '@mui/internal-bundle-size-checker@https://codeload.github.com/mui/mui-public/tar.gz/e81ad6cfa78b3bca1c5de0195604f0544b1a8c2b#path:./packages/bundle-size-checker(webpack-cli@6.0.1(webpack-bundle-analyzer@4.10.2)(webpack@5.98.0))': dependencies: '@aws-sdk/client-s3': 3.787.0 '@aws-sdk/credential-providers': 3.787.0