import { execa } from 'execa'; import { fileURLToPath } from 'url'; import v8 from 'v8'; import dev from '../../packages/astro/dist/core/dev/index.js'; import { loadConfig } from '../../packages/astro/dist/core/config.js'; import prettyBytes from 'pretty-bytes'; /** URL directory containing the entire project. */ const projDir = new URL('./project/', import.meta.url); function mean(numbers) { var total = 0, i; for (i = 0; i < numbers.length; i += 1) { total += numbers[i]; } return total / numbers.length; } function median(numbers) { // median of [3, 5, 4, 4, 1, 1, 2, 3] = 3 var median = 0, numsLen = numbers.length; numbers.sort(); if ( numsLen % 2 === 0 // is even ) { // average of two middle numbers median = (numbers[numsLen / 2 - 1] + numbers[numsLen / 2]) / 2; } else { // is odd // middle number only median = numbers[(numsLen - 1) / 2]; } return median; } let config = await loadConfig({ cwd: fileURLToPath(projDir), }); config.buildOptions.experimentalStaticBuild = true; const server = await dev(config, { logging: 'error' }); // Prime the server so initial memory is created await fetch(`http://localhost:3000/page-0`); const sizes = []; function addSize() { sizes.push(v8.getHeapStatistics().total_heap_size); } async function run() { addSize(); for (let i = 0; i < 100; i++) { let path = `/page-${i}`; await fetch(`http://localhost:3000${path}`); } addSize(); } for (let i = 0; i < 100; i++) { await run(); } let lastThirthy = sizes.slice(sizes.length - 30); let averageOfLastThirty = mean(lastThirthy); let medianOfAll = median(sizes); // If the trailing average is higher than the median, see if it's more than 5% higher if (averageOfLastThirty > medianOfAll) { let percentage = Math.abs(averageOfLastThirty - medianOfAll) / medianOfAll; if (percentage > 0.1) { throw new Error( `The average towards the end (${prettyBytes(averageOfLastThirty)}) is more than 10% higher than the median of all runs (${prettyBytes( medianOfAll )}). This tells us that memory continues to grow and a leak is likely.` ); } } await server.stop();