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 Add a new feature : 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 feat: 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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants