diff --git a/.github/workflows/devtools_regression_tests.yml b/.github/workflows/devtools_regression_tests.yml index 0b70cfaf4e9..13b37f1743b 100644 --- a/.github/workflows/devtools_regression_tests.yml +++ b/.github/workflows/devtools_regression_tests.yml @@ -92,7 +92,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: react-devtools - path: build/devtools.tgz + path: build/devtools if-no-files-found: error # Simplifies getting the extension for local testing - name: Archive chrome extension diff --git a/.github/workflows/runtime_build_and_test.yml b/.github/workflows/runtime_build_and_test.yml index d9fb47da3b2..8ecb956e6b9 100644 --- a/.github/workflows/runtime_build_and_test.yml +++ b/.github/workflows/runtime_build_and_test.yml @@ -766,6 +766,11 @@ jobs: name: react-devtools-${{ matrix.browser }}-extension path: build/devtools/${{ matrix.browser }}-extension.zip if-no-files-found: error + - name: Archive ${{ matrix.browser }} metadata + uses: actions/upload-artifact@v4 + with: + name: react-devtools-${{ matrix.browser }}-metadata + path: build/devtools/webpack-stats.*.json merge_devtools_artifacts: name: Merge DevTools artifacts @@ -776,7 +781,7 @@ jobs: uses: actions/upload-artifact/merge@v4 with: name: react-devtools - pattern: react-devtools-*-extension + pattern: react-devtools-* run_devtools_e2e_tests: name: Run DevTools e2e tests diff --git a/packages/react-devtools-extensions/build.js b/packages/react-devtools-extensions/build.js index 4d95af2a0a1..43bb7472be2 100644 --- a/packages/react-devtools-extensions/build.js +++ b/packages/react-devtools-extensions/build.js @@ -6,7 +6,7 @@ const archiver = require('archiver'); const {execSync} = require('child_process'); const {readFileSync, writeFileSync, createWriteStream} = require('fs'); const {copy, ensureDir, move, remove, pathExistsSync} = require('fs-extra'); -const {join, resolve} = require('path'); +const {join, resolve, basename} = require('path'); const {getGitCommit} = require('./utils'); // These files are copied along with Webpack-bundled files @@ -80,8 +80,25 @@ const build = async (tempPath, manifestPath, envExtension = {}) => { const copiedManifestPath = join(zipPath, 'manifest.json'); + let webpackStatsFilePath = null; // Copy unbuilt source files to zip dir to be packaged: - await copy(binPath, join(zipPath, 'build')); + await copy(binPath, join(zipPath, 'build'), { + filter: filePath => { + if (basename(filePath).startsWith('webpack-stats.')) { + webpackStatsFilePath = filePath; + // The ZIP is the actual extension and doesn't need this metadata. + return false; + } + return true; + }, + }); + if (webpackStatsFilePath !== null) { + await copy( + webpackStatsFilePath, + join(tempPath, basename(webpackStatsFilePath)), + ); + webpackStatsFilePath = join(tempPath, basename(webpackStatsFilePath)); + } await copy(manifestPath, copiedManifestPath); await Promise.all( STATIC_FILES.map(file => copy(join(__dirname, file), join(zipPath, file))), @@ -120,9 +137,11 @@ const build = async (tempPath, manifestPath, envExtension = {}) => { archive.finalize(); zipStream.on('close', () => resolvePromise()); }); + + return webpackStatsFilePath; }; -const postProcess = async (tempPath, destinationPath) => { +const postProcess = async (tempPath, destinationPath, webpackStatsFilePath) => { const unpackedSourcePath = join(tempPath, 'zip'); const packedSourcePath = join(tempPath, 'ReactDevTools.zip'); const packedDestPath = join(destinationPath, 'ReactDevTools.zip'); @@ -130,6 +149,14 @@ const postProcess = async (tempPath, destinationPath) => { await move(unpackedSourcePath, unpackedDestPath); // Copy built files to destination await move(packedSourcePath, packedDestPath); // Copy built files to destination + if (webpackStatsFilePath !== null) { + await move( + webpackStatsFilePath, + join(destinationPath, basename(webpackStatsFilePath)), + ); + } else { + console.log('No webpack-stats.json file was generated.'); + } await remove(tempPath); // Clean up temp directory and files }; @@ -158,10 +185,14 @@ const main = async buildId => { const tempPath = join(__dirname, 'build', buildId); await ensureLocalBuild(); await preProcess(destinationPath, tempPath); - await build(tempPath, manifestPath, envExtension); + const webpackStatsFilePath = await build( + tempPath, + manifestPath, + envExtension, + ); const builtUnpackedPath = join(destinationPath, 'unpacked'); - await postProcess(tempPath, destinationPath); + await postProcess(tempPath, destinationPath, webpackStatsFilePath); return builtUnpackedPath; } catch (error) { diff --git a/packages/react-devtools-extensions/package.json b/packages/react-devtools-extensions/package.json index c48e4d3b195..383f7d42118 100644 --- a/packages/react-devtools-extensions/package.json +++ b/packages/react-devtools-extensions/package.json @@ -65,6 +65,7 @@ "webpack": "^5.82.1", "webpack-cli": "^5.1.1", "webpack-dev-server": "^4.15.0", + "webpack-stats-plugin": "^1.1.3", "workerize-loader": "^2.0.2" }, "dependencies": { diff --git a/packages/react-devtools-extensions/webpack.config.js b/packages/react-devtools-extensions/webpack.config.js index 4a3052517c8..13a37ce25e2 100644 --- a/packages/react-devtools-extensions/webpack.config.js +++ b/packages/react-devtools-extensions/webpack.config.js @@ -6,6 +6,7 @@ const TerserPlugin = require('terser-webpack-plugin'); const {GITHUB_URL, getVersionString} = require('./utils'); const {resolveFeatureFlags} = require('react-devtools-shared/buildUtils'); const SourceMapIgnoreListPlugin = require('react-devtools-shared/SourceMapIgnoreListPlugin'); +const {StatsWriterPlugin} = require('webpack-stats-plugin'); const NODE_ENV = process.env.NODE_ENV; if (!NODE_ENV) { @@ -37,6 +38,21 @@ const IS_INTERNAL_MCP_BUILD = process.env.IS_INTERNAL_MCP_BUILD === 'true'; const featureFlagTarget = process.env.FEATURE_FLAG_TARGET || 'extension-oss'; +let statsFileName = `webpack-stats.${featureFlagTarget}.${__DEV__ ? 'development' : 'production'}`; +if (IS_CHROME) { + statsFileName += `.chrome`; +} +if (IS_FIREFOX) { + statsFileName += `.firefox`; +} +if (IS_EDGE) { + statsFileName += `.edge`; +} +if (IS_INTERNAL_MCP_BUILD) { + statsFileName += `.mcp`; +} +statsFileName += '.json'; + const babelOptions = { configFile: resolve( __dirname, @@ -213,6 +229,10 @@ module.exports = { ); }, }, + new StatsWriterPlugin({ + stats: 'verbose', + filename: statsFileName, + }), ], module: { defaultRules: [ diff --git a/scripts/ci/pack_and_store_devtools_artifacts.sh b/scripts/ci/pack_and_store_devtools_artifacts.sh index 5118b426247..d427a303e5a 100755 --- a/scripts/ci/pack_and_store_devtools_artifacts.sh +++ b/scripts/ci/pack_and_store_devtools_artifacts.sh @@ -20,13 +20,11 @@ cd ../react-devtools-extensions if [[ -n "$1" ]]; then yarn build:$1 mv ./$1/build/ReactDevTools.zip ../../build/devtools/$1-extension.zip + mv ./$1/build/webpack-stats.*.json ../../build/devtools/ else yarn build for browser in chrome firefox edge; do mv ./$browser/build/ReactDevTools.zip ../../build/devtools/$browser-extension.zip + mv ./$browser/build/webpack-stats.*.json ../../build/devtools/ done fi - -# Compress all DevTools artifacts into a single tarball for easy download -cd ../../build/devtools -tar -zcvf ../devtools.tgz . diff --git a/yarn.lock b/yarn.lock index f58b4979fa3..84de7c37826 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17792,6 +17792,11 @@ webpack-sources@^3.2.0, webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== +webpack-stats-plugin@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-1.1.3.tgz#ebcc36c8b468074ad737882e2043c1ce4b55d928" + integrity sha512-yUKYyy+e0iF/w31QdfioRKY+h3jDBRpthexBOWGKda99iu2l/wxYsI/XqdlP5IU58/0KB9CsJZgWNAl+/MPkRw== + webpack@^5.82.1: version "5.82.1" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.82.1.tgz#8f38c78e53467556e8a89054ebd3ef6e9f67dbab"