-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: make bundle analysis work with app dir
- Loading branch information
1 parent
38fc128
commit c7165fa
Showing
4 changed files
with
142 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters