next
Version:
The React Framework
155 lines (154 loc) • 6.38 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "writeRouteBundleStats", {
enumerable: true,
get: function() {
return writeRouteBundleStats;
}
});
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
const _fs = require("fs");
const _constants = require("../shared/lib/constants");
const _utils = require("./utils");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
const ROUTE_BUNDLE_STATS_FILE = 'route-bundle-stats.json';
function sumFileSizes(distDir, files, cache) {
let total = 0;
for (const relPath of files){
const cached = cache.get(relPath);
if (cached !== undefined) {
total += cached;
continue;
}
try {
const size = (0, _fs.statSync)(_path.default.join(distDir, relPath)).size;
cache.set(relPath, size);
total += size;
} catch {
// ignore missing files
}
}
return total;
}
function toProjectRelativePaths(dir, distDir, relPaths) {
return relPaths.map((f)=>_path.default.relative(dir, _path.default.join(distDir, f)));
}
function buildRouteToAppPathsMap(appPathsManifest) {
const { normalizeAppPath } = require('../shared/lib/router/utils/app-paths');
// Keys in appPathsManifest are app paths like /blog/[slug]/page;
// values are server bundle file paths. Normalize the key to get the route.
const routeToAppPaths = new Map();
for (const appPath of Object.keys(appPathsManifest)){
const route = normalizeAppPath(appPath);
const existing = routeToAppPaths.get(route);
if (existing) {
existing.push(appPath);
} else {
routeToAppPaths.set(route, [
appPath
]);
}
}
return routeToAppPaths;
}
// Reads the manfiest file and gets the entry JS files. The manifest file is
// a JavaScript file that sets a global variable (__RSC_MANIFEST). We require()
// it with a save/restore of the global
function readEntryJSFiles(distDir, pagePath, appRoute) {
const manifestFile = _path.default.join(distDir, _constants.SERVER_DIRECTORY, 'app', `${pagePath}_${_constants.CLIENT_REFERENCE_MANIFEST}.js`);
try {
const g = global;
const prev = g.__RSC_MANIFEST;
g.__RSC_MANIFEST = undefined;
require(manifestFile);
const rscManifest = g.__RSC_MANIFEST;
g.__RSC_MANIFEST = prev;
// The key in __RSC_MANIFEST is the app path (e.g. /blog/[slug]/page)
const manifestEntry = (rscManifest == null ? void 0 : rscManifest[pagePath]) ?? (rscManifest == null ? void 0 : rscManifest[appRoute]);
return manifestEntry == null ? void 0 : manifestEntry.entryJSFiles;
} catch {
return undefined;
}
}
function collectPagesRouterStats(pages, buildManifest, distDir, dir, cache) {
const rows = [];
const sharedFiles = buildManifest.pages['/_app'] ?? [];
for (const page of (0, _utils.filterAndSortList)(pages, 'pages', false)){
if (page === '/_app' || page === '/_document' || page === '/_error') continue;
const allFiles = (buildManifest.pages[page] ?? []).filter((f)=>f.endsWith('.js'));
const sharedJs = sharedFiles.filter((f)=>f.endsWith('.js'));
const chunks = [
...new Set([
...allFiles,
...sharedJs
])
];
const firstLoadUncompressedJsBytes = sumFileSizes(distDir, chunks, cache);
rows.push({
route: page,
firstLoadUncompressedJsBytes,
firstLoadChunkPaths: toProjectRelativePaths(dir, distDir, chunks)
});
}
return rows;
}
async function collectAppRouterStats(appRoutes, buildManifest, distDir, dir, cache) {
let appPathsManifest = {};
try {
const manifestPath = _path.default.join(distDir, _constants.SERVER_DIRECTORY, _constants.APP_PATHS_MANIFEST);
appPathsManifest = JSON.parse(await _fs.promises.readFile(manifestPath, 'utf8'));
} catch {
// App paths manifest not available; skip app router sizes
return [];
}
const routeToAppPaths = buildRouteToAppPathsMap(appPathsManifest);
const sharedFiles = buildManifest.rootMainFiles ?? [];
const rows = [];
for (const appRoute of (0, _utils.filterAndSortList)(appRoutes, 'app', false)){
const appPaths = routeToAppPaths.get(appRoute) ?? [];
// Find the /page entry (most specific, has the most chunks)
const pagePath = appPaths.find((p)=>p.endsWith('/page'));
if (!pagePath) continue;
const entryJSFiles = readEntryJSFiles(distDir, pagePath, appRoute);
if (!entryJSFiles) continue;
// Union JS files across all segments (page, layout, etc.) so that
// layout code's contribution is included in the First Load JS total.
const allFiles = [
...new Set(Object.values(entryJSFiles).flat().filter((f)=>f.endsWith('.js')))
];
const sharedJs = sharedFiles.filter((f)=>f.endsWith('.js'));
const chunks = [
...new Set([
...allFiles,
...sharedJs
])
];
const firstLoadUncompressedJsBytes = sumFileSizes(distDir, chunks, cache);
rows.push({
route: appRoute,
firstLoadUncompressedJsBytes,
firstLoadChunkPaths: toProjectRelativePaths(dir, distDir, chunks)
});
}
return rows;
}
async function writeRouteBundleStats(lists, buildManifest, distDir, dir) {
const cache = new Map();
const rows = [
...lists.pages.length > 0 ? collectPagesRouterStats(lists.pages, buildManifest, distDir, dir, cache) : [],
...lists.app && lists.app.length > 0 ? await collectAppRouterStats(lists.app, buildManifest, distDir, dir, cache) : []
];
rows.sort((a, b)=>b.firstLoadUncompressedJsBytes - a.firstLoadUncompressedJsBytes);
const diagnosticsDir = _path.default.join(distDir, 'diagnostics');
await _fs.promises.mkdir(diagnosticsDir, {
recursive: true
});
await _fs.promises.writeFile(_path.default.join(diagnosticsDir, ROUTE_BUNDLE_STATS_FILE), JSON.stringify(rows, null, 2));
}
//# sourceMappingURL=route-bundle-stats.js.map