feat: make bundle analysis work with app dir
This commit is contained in:
parent
38fc128d62
commit
c7165fa2dc
4 changed files with 142 additions and 2 deletions
2
.github/workflows/nextjs_bundle_analysis.yml
vendored
2
.github/workflows/nextjs_bundle_analysis.yml
vendored
|
|
@ -51,7 +51,7 @@ jobs:
|
||||||
# Here's the first place where next-bundle-analysis' own script is used
|
# Here's the first place where next-bundle-analysis' own script is used
|
||||||
# This step pulls the raw bundle stats for the current bundle
|
# This step pulls the raw bundle stats for the current bundle
|
||||||
- name: Analyze bundle
|
- name: Analyze bundle
|
||||||
run: npx -p nextjs-bundle-analysis report
|
run: node report-bundle-size.js
|
||||||
|
|
||||||
- name: Upload bundle
|
- name: Upload bundle
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
|
|
||||||
|
|
@ -97,8 +97,10 @@
|
||||||
"eslint-plugin-storybook": "^0.6.15",
|
"eslint-plugin-storybook": "^0.6.15",
|
||||||
"eslint-plugin-tailwindcss": "^3.13.0",
|
"eslint-plugin-tailwindcss": "^3.13.0",
|
||||||
"fetch-mock": "^9.11.0",
|
"fetch-mock": "^9.11.0",
|
||||||
|
"gzip-size": "6",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"mkdirp": "^3.0.1",
|
||||||
"npm-only-allow": "^1.2.6",
|
"npm-only-allow": "^1.2.6",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
|
|
|
||||||
133
report-bundle-size.js
Normal file
133
report-bundle-size.js
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// edited to work with the appdir by @raphaelbadia
|
||||||
|
|
||||||
|
const gzSize = require("gzip-size")
|
||||||
|
const mkdirp = require("mkdirp")
|
||||||
|
const fs = require("fs")
|
||||||
|
const path = require("path")
|
||||||
|
|
||||||
|
// Pull options from `package.json`
|
||||||
|
const options = getOptions()
|
||||||
|
const BUILD_OUTPUT_DIRECTORY = getBuildOutputDirectory(options)
|
||||||
|
|
||||||
|
// first we check to make sure that the build output directory exists
|
||||||
|
const nextMetaRoot = path.join(process.cwd(), BUILD_OUTPUT_DIRECTORY)
|
||||||
|
try {
|
||||||
|
fs.accessSync(nextMetaRoot, fs.constants.R_OK)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
`No build output found at "${nextMetaRoot}" - you may not have your working directory set correctly, or not have run "next build".`
|
||||||
|
)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if so, we can import the build manifest
|
||||||
|
const buildMeta = require(path.join(nextMetaRoot, "build-manifest.json"))
|
||||||
|
const appDirMeta = require(path.join(nextMetaRoot, "app-build-manifest.json"))
|
||||||
|
|
||||||
|
// this memory cache ensures we dont read any script file more than once
|
||||||
|
// bundles are often shared between pages
|
||||||
|
const memoryCache = {}
|
||||||
|
|
||||||
|
// since _app is the template that all other pages are rendered into,
|
||||||
|
// every page must load its scripts. we'll measure its size here
|
||||||
|
const globalBundle = buildMeta.pages["/_app"]
|
||||||
|
const globalBundleSizes = getScriptSizes(globalBundle)
|
||||||
|
|
||||||
|
// next, we calculate the size of each page's scripts, after
|
||||||
|
// subtracting out the global scripts
|
||||||
|
const allPageSizes = Object.values(buildMeta.pages).reduce((acc, scriptPaths, i) => {
|
||||||
|
const pagePath = Object.keys(buildMeta.pages)[i]
|
||||||
|
const scriptSizes = getScriptSizes(scriptPaths.filter((scriptPath) => !globalBundle.includes(scriptPath)))
|
||||||
|
|
||||||
|
acc[pagePath] = scriptSizes
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
const globalAppDirBundle = buildMeta.rootMainFiles
|
||||||
|
const globalAppDirBundleSizes = getScriptSizes(globalAppDirBundle)
|
||||||
|
|
||||||
|
const allAppDirSizes = Object.values(appDirMeta.pages).reduce((acc, scriptPaths, i) => {
|
||||||
|
const pagePath = Object.keys(appDirMeta.pages)[i]
|
||||||
|
const scriptSizes = getScriptSizes(scriptPaths.filter((scriptPath) => !globalAppDirBundle.includes(scriptPath)))
|
||||||
|
acc[pagePath] = scriptSizes
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
// format and write the output
|
||||||
|
const rawData = JSON.stringify({
|
||||||
|
...allAppDirSizes,
|
||||||
|
__global: globalAppDirBundleSizes,
|
||||||
|
})
|
||||||
|
|
||||||
|
// log ouputs to the gh actions panel
|
||||||
|
console.log(rawData)
|
||||||
|
|
||||||
|
mkdirp.sync(path.join(nextMetaRoot, "analyze/"))
|
||||||
|
fs.writeFileSync(path.join(nextMetaRoot, "analyze/__bundle_analysis.json"), rawData)
|
||||||
|
|
||||||
|
// --------------
|
||||||
|
// Util Functions
|
||||||
|
// --------------
|
||||||
|
|
||||||
|
// given an array of scripts, return the total of their combined file sizes
|
||||||
|
function getScriptSizes(scriptPaths) {
|
||||||
|
const res = scriptPaths.reduce(
|
||||||
|
(acc, scriptPath) => {
|
||||||
|
const [rawSize, gzipSize] = getScriptSize(scriptPath)
|
||||||
|
acc.raw += rawSize
|
||||||
|
acc.gzip += gzipSize
|
||||||
|
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
{ raw: 0, gzip: 0 }
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// given an individual path to a script, return its file size
|
||||||
|
function getScriptSize(scriptPath) {
|
||||||
|
const encoding = "utf8"
|
||||||
|
const p = path.join(nextMetaRoot, scriptPath)
|
||||||
|
|
||||||
|
let rawSize, gzipSize
|
||||||
|
if (Object.keys(memoryCache).includes(p)) {
|
||||||
|
rawSize = memoryCache[p][0]
|
||||||
|
gzipSize = memoryCache[p][1]
|
||||||
|
} else {
|
||||||
|
const textContent = fs.readFileSync(p, encoding)
|
||||||
|
rawSize = Buffer.byteLength(textContent, encoding)
|
||||||
|
gzipSize = gzSize.sync(textContent)
|
||||||
|
memoryCache[p] = [rawSize, gzipSize]
|
||||||
|
}
|
||||||
|
|
||||||
|
return [rawSize, gzipSize]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads options from `package.json`
|
||||||
|
*/
|
||||||
|
function getOptions(pathPrefix = process.cwd()) {
|
||||||
|
const pkg = require(path.join(pathPrefix, "package.json"))
|
||||||
|
|
||||||
|
return { ...pkg.nextBundleAnalysis, name: pkg.name }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the output build directory, defaults to `.next`
|
||||||
|
*
|
||||||
|
* @param {object} options the options parsed from package.json.nextBundleAnalysis using `getOptions`
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getBuildOutputDirectory(options) {
|
||||||
|
return options.buildOutputDirectory || ".next"
|
||||||
|
}
|
||||||
|
|
@ -9419,7 +9419,7 @@ gunzip-maybe@^1.4.2:
|
||||||
pumpify "^1.3.3"
|
pumpify "^1.3.3"
|
||||||
through2 "^2.0.3"
|
through2 "^2.0.3"
|
||||||
|
|
||||||
gzip-size@^6.0.0:
|
gzip-size@6, gzip-size@^6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
|
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
|
||||||
integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==
|
integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==
|
||||||
|
|
@ -11813,6 +11813,11 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||||
|
|
||||||
|
mkdirp@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50"
|
||||||
|
integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==
|
||||||
|
|
||||||
module-details-from-path@^1.0.3:
|
module-details-from-path@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b"
|
resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue