import { h as defineCommand, i as extendsArgs, l as rootDirArgs, n as dotEnvArgs, s as logLevelArgs } from "./_shared-YyGADOl7.mjs";
import { S as ActionableError, c as intro$1, d as outro$1, u as logger } from "./startup-clock-DXZlxuSA.mjs";
import { n as resolveDotenvFileNames } from "./args-DGaOocsH.mjs";
import { t as overrideEnv } from "./env-BkMPEX1D.mjs";
import { t as isAllowedHost } from "./host-check-DW342-PP.mjs";
import { n as clearDir } from "./fs-CSqg02ri.mjs";
import { t as loadKit } from "./kit-CbhDo9Zs.mjs";
import { a as formatLockError, n as acquireOutputLock, t as acquireLock } from "./lockfile-BZHfAuKg.mjs";
import { t as resolveServerBuild } from "./server-build-Cga7EB-K.mjs";
import { i as resolveRootDir, r as relativeToProcess } from "./paths-DlXZaaIg.mjs";
import process from "node:process";
import { styleText } from "node:util";
import { note, taskLog } from "@clack/prompts";
import { join, relative } from "pathe";
import { promises } from "node:fs";
import { defu as defu$1 } from "defu";
import { serve } from "srvx";
//#region src/commands/analyze.ts
const NON_WORD_RE = /[^\w-]/g;
const indexHtml = `
Nuxt Bundle Stats (experimental)
Nuxt Bundle Stats (experimental)
`.trim();
var analyze_default = defineCommand({
meta: {
name: "analyze",
description: "Build Nuxt and analyze production bundle (experimental)"
},
args: {
...rootDirArgs,
...logLevelArgs,
...dotEnvArgs,
...extendsArgs,
name: {
type: "string",
description: "Name of the analysis",
default: "default",
valueHint: "name"
},
serve: {
type: "boolean",
description: "Serve the analysis results",
negativeDescription: "Skip serving the analysis results",
default: true
},
prerender: {
type: "boolean",
description: "Prerender routes while analyzing",
default: false
}
},
async run(ctx) {
overrideEnv("production");
const cwd = resolveRootDir(ctx.args);
const name = ctx.args.name || "default";
const slug = name.trim().replace(NON_WORD_RE, "_") || "default";
intro$1(styleText("cyan", "Analyzing bundle size..."));
const startTime = Date.now();
const kit = await loadKit(cwd);
const { loadNuxt, buildNuxt } = kit;
const nuxt = await loadNuxt({
cwd,
ready: false,
dotenv: {
cwd,
fileName: resolveDotenvFileNames(ctx.args.dotenv)
},
overrides: defu$1(ctx.data?.overrides, {
...ctx.args.extends.length > 0 && { extends: ctx.args.extends },
build: { analyze: { enabled: true } },
vite: { build: { rollupOptions: { output: {
chunkFileNames: "_nuxt/[name].js",
entryFileNames: "_nuxt/[name].js"
} } } },
logLevel: ctx.args.logLevel,
...!ctx.args.prerender && { nitro: { prerender: {
routes: [],
crawlLinks: false,
ignore: [() => true]
} } }
})
});
let skippedPrerenderRoutes = 0;
if (!ctx.args.prerender) nuxt.hook("nitro:init", (nitro) => {
nitro.hooks.hook("prerender:routes", (routes) => {
skippedPrerenderRoutes = routes.size;
routes.clear();
});
});
await nuxt.ready();
const analyzeDir = nuxt.options.analyzeDir;
const buildDir = nuxt.options.buildDir;
const serverBuild = resolveServerBuild(kit, nuxt);
const outDir = serverBuild.dir;
nuxt.options.build.analyze = defu$1(nuxt.options.build.analyze, { filename: join(analyzeDir, "client.html") });
const lockInfo = {
command: "analyze",
cwd
};
const lock = acquireLock(buildDir, lockInfo);
if (lock.existing) throw new ActionableError(formatLockError(lock.existing));
const outputLock = acquireOutputLock(nuxt.options.rootDir, outDir, lockInfo);
if (outputLock.existing) {
lock.release();
throw new ActionableError(formatLockError(outputLock.existing, { outputDir: relative(process.cwd(), outDir) }));
}
try {
const tasklog = taskLog({
title: "Building Nuxt with analysis enabled",
retainLog: false,
limit: 1
});
tasklog.message("Clearing analyze directory...");
await clearDir(analyzeDir);
tasklog.message("Building Nuxt...");
await buildNuxt(nuxt);
tasklog.success("Build complete");
if (skippedPrerenderRoutes > 0) logger.info(`Skipped prerendering ${skippedPrerenderRoutes} route${skippedPrerenderRoutes === 1 ? "" : "s"}. Pass ${styleText("cyan", "--prerender")} to include assets emitted while prerendering.`);
const meta = {
name,
slug,
startTime,
endTime: Date.now(),
analyzeDir,
buildDir,
outDir: serverBuild.dir
};
await nuxt.callHook("build:analyze:done", meta);
await promises.writeFile(join(analyzeDir, "meta.json"), JSON.stringify(meta, null, 2), "utf-8");
} finally {
outputLock.release();
lock.release();
}
note(`${relativeToProcess(analyzeDir)}\n\nDo not deploy analyze results! Use ${styleText("cyan", "nuxt build")} before deploying.`, "Build location");
if (ctx.args.serve !== false && !process.env.CI) {
const headers = { "content-type": "text/html" };
const readReport = (name) => promises.readFile(join(analyzeDir, name), "utf8").catch(() => void 0);
const reports = /* @__PURE__ */ new Map([["/client", await readReport("client.html")], ["/nitro", await readReport("nitro.html")]]);
logger.step("Starting stats server...");
const hostname = process.env.HOST || "localhost";
const allowedHosts = /* @__PURE__ */ new Set([hostname.toLowerCase()]);
await serve({
hostname,
fetch(request) {
if (!isAllowedHost(request.headers.get("host") ?? void 0, allowedHosts)) return new Response("Forbidden: this host is not allowed.", {
status: 403,
headers: { "content-type": "text/plain" }
});
const pathname = new URL(request.url).pathname.replace(/\/$/, "");
if (reports.has(pathname)) {
const report = reports.get(pathname);
return report === void 0 ? new Response("This report was not generated by the analyze build.", {
status: 404,
headers
}) : new Response(report, { headers });
}
return new Response(indexHtml, { headers });
}
}).ready();
} else outro$1("✨ Analysis build complete!");
}
});
//#endregion
export { analyze_default as default };