Skip to content

Conversation

@KARANsinghBISHTT
Copy link

@KARANsinghBISHTT KARANsinghBISHTT commented Jan 25, 2026

Problem

Vite's build command cannot output build statistics in a machine-readable format. Adding build stats feature with tests and CLI options

Expected Behavior

CLI:

  • vite build --json - Output JSON to stdout, suppress normal logs
  • vite build --json <filename> - Write JSON to file, create parent directories

Output Structure:

{
"version": "...",
"timestamp": ...,
"duration": ...,
"success": true,
"outputs": {
"": {
"outDir": "...",
"chunks": [
{
"name": "...",
"type": "chunk",
"size": ...,
"gzipSize": ...,
"isEntry": ...,
"isDynamicEntry": ...,
"modules": [...],
"imports": [...],
"dynamicImports": [...]
}
],
"assets": [
{
"name": "...",
"type": "asset",
"size": ...,
"gzipSize": ...
}
],
"totals": {
"chunkCount": ...,
"assetCount": ...,
"totalSize": ...,
"totalGzipSize": ...
}
}
},
"warnings": [{ "message": "..." }],
"errors": [{ "message": "..." }]
}

Requirements:

  • 2-space indentation
  • Exclude *.map files
  • Sort chunks/assets by size descending
  • gzipSize is null for non-compressible files or when build.reportCompressedSize is false
  • totalSize is sum of all chunk and asset sizes
  • totalGzipSize is sum of all non-null gzip sizes (null when build.reportCompressedSize is false)
  • Output JSON even on build failure with success: false
  • outDir must be absolute path
@KARANsinghBISHTT KARANsinghBISHTT changed the title Add build stats functionality with tests and CLI options Jan 25, 2026
@KARANsinghBISHTT KARANsinghBISHTT changed the title Add a new feature : build stats functionality with tests and CLI options Jan 25, 2026
@bluwy
Copy link
Member

bluwy commented Jan 26, 2026

Can you explain why you need this? Could you write a Vite plugin that extracts some of the information instead? Or use the manifest option?

Comment on lines +386 to +410
if (jsonOutput && builder._buildStats) {
const { writeBuildStats } = await import('./buildStats')
await writeBuildStats(
builder._buildStats.getStats(true),
jsonOutput === true ? true : jsonOutput,
)
}
} catch (e) {
createLogger(options.logLevel).error(
colors.red(`error during build:\n${e.stack}`),
{ error: e },
)
// If JSON output is requested, output error as JSON
if (jsonOutput) {
const { BuildStatsCollector, writeBuildStats } =
await import('./buildStats')
const errorCollector = new BuildStatsCollector(false)
errorCollector.start()
errorCollector.addError(e.message || 'Build failed')
await writeBuildStats(
errorCollector.getStats(false),
jsonOutput === true ? true : jsonOutput,
)
} else {
createLogger(options.logLevel).error(
colors.red(`error during build:\n${e.stack}`),
{ error: e },
)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error handling logic is broken. If writeBuildStats() fails after a successful build, the catch block will create a new stats object with the write error message instead of the build stats, resulting in incorrect JSON output that reports build failure when the build actually succeeded. The try-catch should be restructured to separate build errors from stats writing errors.

try {
  const inlineConfig: InlineConfig = { /* ... */ }
  const builder = await createBuilder(inlineConfig, null)
  await builder.buildApp()

  // Output JSON stats if requested
  if (jsonOutput && builder._buildStats) {
    const { writeBuildStats } = await import('./buildStats')
    try {
      await writeBuildStats(
        builder._buildStats.getStats(true),
        jsonOutput === true ? true : jsonOutput,
      )
    } catch (writeError) {
      // Handle stats writing error separately
      if (!jsonOutput || jsonOutput !== true) {
        createLogger(options.logLevel).error(
          colors.red(`Failed to write stats: ${writeError.message}`),
        )
      }
      process.exit(1)
    }
  }
} catch (e) {
  // Build error handling
  if (jsonOutput) {
    const { BuildStatsCollector, writeBuildStats } = await import('./buildStats')
    const errorCollector = new BuildStatsCollector(false)
    errorCollector.start()
    errorCollector.addError(e.message || 'Build failed')
    await writeBuildStats(
      errorCollector.getStats(false),
      jsonOutput === true ? true : jsonOutput,
    )
  } else {
    createLogger(options.logLevel).error(
      colors.red(`error during build:\n${e.stack}`),
      { error: e },
    )
  }
  process.exit(1)
}
Suggested change
if (jsonOutput && builder._buildStats) {
const { writeBuildStats } = await import('./buildStats')
await writeBuildStats(
builder._buildStats.getStats(true),
jsonOutput === true ? true : jsonOutput,
)
}
} catch (e) {
createLogger(options.logLevel).error(
colors.red(`error during build:\n${e.stack}`),
{ error: e },
)
// If JSON output is requested, output error as JSON
if (jsonOutput) {
const { BuildStatsCollector, writeBuildStats } =
await import('./buildStats')
const errorCollector = new BuildStatsCollector(false)
errorCollector.start()
errorCollector.addError(e.message || 'Build failed')
await writeBuildStats(
errorCollector.getStats(false),
jsonOutput === true ? true : jsonOutput,
)
} else {
createLogger(options.logLevel).error(
colors.red(`error during build:\n${e.stack}`),
{ error: e },
)
}
if (jsonOutput && builder._buildStats) {
const { writeBuildStats } = await import('./buildStats')
try {
await writeBuildStats(
builder._buildStats.getStats(true),
jsonOutput === true ? true : jsonOutput,
)
} catch (writeError) {
// Handle stats writing error separately
createLogger(options.logLevel).error(
colors.red(`Failed to write stats: ${writeError.message}`),
)
process.exit(1)
}
}
} catch (e) {
// If JSON output is requested, output error as JSON
if (jsonOutput) {
const { BuildStatsCollector, writeBuildStats } =
await import('./buildStats')
const errorCollector = new BuildStatsCollector(false)
errorCollector.start()
errorCollector.addError(e.message || 'Build failed')
await writeBuildStats(
errorCollector.getStats(false),
jsonOutput === true ? true : jsonOutput,
)
} else {
createLogger(options.logLevel).error(
colors.red(`error during build:\n${e.stack}`),
{ error: e },
)
}
process.exit(1)

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants