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
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"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue