UNPKG

next

Version:

The React Framework

149 lines (148 loc) • 6.25 kB
import { setGlobal } from '../../trace'; import * as Log from '../output/log'; import * as path from 'node:path'; import loadConfig from '../../server/config'; import { PHASE_ANALYZE } from '../../shared/lib/constants'; import { turbopackAnalyze } from '../turbopack-analyze'; import { durationToString } from '../duration-to-string'; import { Lockfile } from '../lockfile'; import { installBindings } from '../swc/install-bindings'; import { cpSync, mkdirSync, rmSync } from 'node:fs'; import { readAnalyzeRoutes, writeAnalyzeSnapshot, snapshotNameSchema } from './snapshot'; import http from 'node:http'; // @ts-expect-error types are in @types/serve-handler import serveHandler from 'next/dist/compiled/serve-handler'; import { Telemetry } from '../../telemetry/storage'; import { eventAnalyzeCompleted } from '../../telemetry/events'; import { traceGlobals } from '../../trace/shared'; import { Bundler } from '../../lib/bundler'; export default async function analyze({ dir, reactProductionProfiling = false, noMangling = false, appDirOnly = false, output = false, port = 4000, snapshot }) { if (snapshot !== undefined) snapshotNameSchema.parse(snapshot); let lockfile; try { var _config_experimental; // analyze is Turbopack-only. Mirror what parseBundlerArgs does for build/dev // so every process.env.TURBOPACK consumer in this run agrees with the bundler choice. process.env.TURBOPACK ??= '1'; const config = await loadConfig(PHASE_ANALYZE, dir, { silent: false, reactProductionProfiling, bundler: Bundler.Turbopack }); process.env.NEXT_DEPLOYMENT_ID = config.deploymentId || ''; const distDir = path.join(dir, config.distDir); const telemetry = new Telemetry({ distDir }); setGlobal('phase', PHASE_ANALYZE); setGlobal('distDir', distDir); setGlobal('telemetry', telemetry); // Native locks require synchronous access to bindings. Like next build, // install them before acquiring the lock, but still lock before writes. await installBindings((_config_experimental = config.experimental) == null ? void 0 : _config_experimental.useWasmBinary); // Lock the directory while capture reads/writes it. Static serving and // saved-data replay do not need to hold the capture lock. if (config.experimental.lockDistDir) { mkdirSync(distDir, { recursive: true }); lockfile = await Lockfile.acquireWithRetriesOrExit(path.join(distDir, 'lock'), 'next analyze'); } Log.info('Analyzing a production build...'); const analyzeContext = { config, dir, distDir, noMangling, appDirOnly }; // Start a fresh live dataset so removed routes cannot leak into a replacement. rmSync(path.join(distDir, 'diagnostics/analyze/data'), { recursive: true, force: true, maxRetries: 3 }); const { duration: analyzeDuration, shutdownPromise } = await turbopackAnalyze(analyzeContext); const durationString = durationToString(analyzeDuration); const analyzeDir = path.join(distDir, 'diagnostics/analyze'); await shutdownPromise; cpSync(path.join(__dirname, '../../bundle-analyzer'), analyzeDir, { recursive: true }); mkdirSync(path.join(analyzeDir, 'data'), { recursive: true }); const routes = readAnalyzeRoutes(path.join(analyzeDir, 'data')); // Capture this build alongside any prior builds so the analyzer UI can // offer it as a comparison baseline in the future. const metadata = writeAnalyzeSnapshot({ projectDir: dir, analyzeDir, routes, appDirOnly, noMangling, snapshot }); let logMessage = `Analyze completed in ${durationString}.`; if (output) { logMessage += ` Results written to ${analyzeDir}.\nTo explore the analyze results interactively, run \`next analyze\` without \`--output\`.`; } Log.event(logMessage); telemetry.record(eventAnalyzeCompleted({ success: true, durationInSeconds: Math.round(analyzeDuration), totalPageCount: routes.length })); if (!output) { await startServer(analyzeDir, port); } return metadata; } catch (e) { const telemetry = traceGlobals.get('telemetry'); if (telemetry) { telemetry.record(eventAnalyzeCompleted({ success: false })); } throw e; } finally{ await (lockfile == null ? void 0 : lockfile.unlock()); } } function startServer(dir, port) { const server = http.createServer((req, res)=>{ return serveHandler(req, res, { public: dir }); }); return new Promise((resolve, reject)=>{ function onError(err) { server.close(()=>{ reject(err); }); } server.on('error', onError); server.listen(port, 'localhost', ()=>{ const address = server.address(); if (address == null) { reject(new Error('Unable to get server address')); return; } // No longer needed after startup server.removeListener('error', onError); let addressString; if (typeof address === 'string') { addressString = address; } else if (address.family === 'IPv6' && (address.address === '::' || address.address === '::1')) { addressString = `localhost:${address.port}`; } else if (address.family === 'IPv6') { addressString = `[${address.address}]:${address.port}`; } else { addressString = `${address.address}:${address.port}`; } Log.info(`Bundle analyzer available at http://${addressString}`); resolve(); }); }); } //# sourceMappingURL=index.js.map