101 lines
2.6 KiB
JavaScript
101 lines
2.6 KiB
JavaScript
import { statSync } from "node:fs";
|
|
import { readFile } from "node:fs/promises";
|
|
import { resolve } from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
|
|
const INITIAL_BUNDLE_BUDGET_BYTES = 500 * 1024;
|
|
|
|
function manifestEntries(manifest) {
|
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
throw new Error("Vite manifest must be an object");
|
|
}
|
|
return Object.entries(manifest);
|
|
}
|
|
|
|
function assetFile(entry, entryName) {
|
|
if (!entry || typeof entry.file !== "string" || !entry.file.endsWith(".js")) {
|
|
throw new Error(
|
|
`Manifest entry '${entryName}' must reference a JavaScript file`,
|
|
);
|
|
}
|
|
return entry.file;
|
|
}
|
|
|
|
function getInitialAssetFiles(manifest) {
|
|
const entries = manifestEntries(manifest);
|
|
const entryCandidates = entries.filter(
|
|
([, entry]) => entry?.isEntry === true,
|
|
);
|
|
if (entryCandidates.length !== 1) {
|
|
throw new Error("Vite manifest must contain exactly one JavaScript entry");
|
|
}
|
|
|
|
const files = [];
|
|
const visitedEntries = new Set();
|
|
const visit = (entryName) => {
|
|
if (visitedEntries.has(entryName)) return;
|
|
visitedEntries.add(entryName);
|
|
const entry = manifest[entryName];
|
|
if (!entry)
|
|
throw new Error(`Vite manifest is missing eager import '${entryName}'`);
|
|
files.push(assetFile(entry, entryName));
|
|
for (const importedEntry of entry.imports ?? []) visit(importedEntry);
|
|
};
|
|
|
|
visit(entryCandidates[0][0]);
|
|
return files;
|
|
}
|
|
|
|
function measureAssets(files, getSize) {
|
|
return files.reduce((total, file) => {
|
|
const size = getSize(file);
|
|
if (!Number.isSafeInteger(size) || size < 0) {
|
|
throw new Error(`Cannot measure bundle asset '${file}'`);
|
|
}
|
|
return total + size;
|
|
}, 0);
|
|
}
|
|
|
|
function assertBundleBudget(initialBytes) {
|
|
if (initialBytes > INITIAL_BUNDLE_BUDGET_BYTES) {
|
|
throw new Error(
|
|
`Initial JavaScript payload is ${initialBytes} bytes; budget is ${INITIAL_BUNDLE_BUDGET_BYTES} bytes`,
|
|
);
|
|
}
|
|
}
|
|
|
|
const distributionDirectory = resolve("dist");
|
|
const manifestPath = resolve(distributionDirectory, "bundle-manifest.json");
|
|
|
|
async function loadJson(path) {
|
|
return JSON.parse(await readFile(path, "utf8"));
|
|
}
|
|
|
|
function getAssetSize(file) {
|
|
return statSync(resolve(distributionDirectory, file)).size;
|
|
}
|
|
|
|
async function run() {
|
|
const manifest = await loadJson(manifestPath);
|
|
const initialBytes = measureAssets(
|
|
getInitialAssetFiles(manifest),
|
|
getAssetSize,
|
|
);
|
|
assertBundleBudget(initialBytes);
|
|
console.log(`Initial JavaScript payload: ${initialBytes} bytes`);
|
|
}
|
|
|
|
if (
|
|
process.argv[1] &&
|
|
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
) {
|
|
await run();
|
|
}
|
|
|
|
export {
|
|
assertBundleBudget,
|
|
getInitialAssetFiles,
|
|
INITIAL_BUNDLE_BUDGET_BYTES,
|
|
measureAssets,
|
|
};
|