next.js/scripts/analyze-dev-server-bundle.js
analyze-dev-server-bundle.js143 lines3.9 KB
#!/usr/bin/env node
/**
 * Dev Server Bundle Analyzer
 *
 * Generates a bundle analyzer report for the dev server bundle.
 *
 * Usage:
 *   node scripts/analyze-bundle.js [options]
 *
 * Options:
 *   --open          Open the report in browser (default: false)
 *   --verbose       Show detailed module reasons
 *   --json          Also output stats.json file
 *   --list-modules  List all bundled modules to console
 *   --list-externals List all externalized modules
 */

const { execSync } = require('child_process')
const path = require('path')
const fs = require('fs')

// Parse arguments
const args = process.argv.slice(2)
const hasFlag = (name) => args.includes(`--${name}`)

const openBrowser = hasFlag('open')
const verbose = hasFlag('verbose')
const outputJson = hasFlag('json')
const listModules = hasFlag('list-modules')
const listExternals = hasFlag('list-externals')

const nextDir = path.join(__dirname, '..', 'packages', 'next')
const bundlePath = path.join(
  nextDir,
  'dist/compiled/dev-server/start-server.js'
)
const reportPath = path.join(
  nextDir,
  'dist/compiled/dev-server/bundle-report.html'
)

console.log('\x1b[34m=== Dev Server Bundle Analyzer ===\x1b[0m')
console.log('')

// Build with analyzer
console.log('Building bundle with analyzer...')
const env = {
  ...process.env,
  ANALYZE: '1',
  ...(verbose ? { ANALYZE_REASONS: '1' } : {}),
}

try {
  execSync('npx taskr next_bundle_dev_server', {
    cwd: nextDir,
    stdio: verbose ? 'inherit' : 'pipe',
    env,
  })
} catch (err) {
  console.error('\x1b[31mBuild failed\x1b[0m')
  process.exit(1)
}

// Get bundle stats
const stats = fs.statSync(bundlePath)
const sizeKB = Math.round(stats.size / 1024)
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2)

console.log('')
console.log('\x1b[32mBundle Stats:\x1b[0m')
console.log(`  Size: ${sizeKB} KB (${sizeMB} MB)`)
console.log(`  Path: ${bundlePath}`)
console.log(`  Report: ${reportPath}`)
console.log('')

// List bundled modules
if (listModules) {
  console.log('\x1b[33mBundled Modules:\x1b[0m')
  const content = fs.readFileSync(bundlePath, 'utf-8')
  const moduleMatches = content.match(/"\.\/dist\/[^"]+/g) || []
  const modules = [...new Set(moduleMatches)]
    .map((m) => m.replace(/^"/, ''))
    .filter((m) => !m.includes(' recursive'))
    .sort()

  modules.forEach((m) => console.log(`  ${m}`))
  console.log(`\n  Total: ${modules.length} modules`)
  console.log('')
}

// List externalized modules
if (listExternals) {
  console.log('\x1b[33mExternalized Modules:\x1b[0m')
  const content = fs.readFileSync(bundlePath, 'utf-8')

  // Find external requires
  const externalMatches =
    content.match(
      /require\("(next\/dist\/[^"]+|@next\/[^"]+|styled-jsx[^"]*)"\)/g
    ) || []
  const externals = [...new Set(externalMatches)]
    .map((m) => m.match(/require\("([^"]+)"\)/)[1])
    .sort()

  externals.forEach((m) => console.log(`  ${m}`))
  console.log(`\n  Total: ${externals.length} external requires`)
  console.log('')
}

// Output JSON stats
if (outputJson) {
  const statsJsonPath = path.join(
    nextDir,
    'dist/compiled/dev-server/stats.json'
  )
  console.log(`Stats JSON: ${statsJsonPath}`)
  console.log('(Run with ANALYZE_REASONS=1 for detailed stats)')
}

// Open in browser
if (openBrowser) {
  console.log('Opening report in browser...')
  const opener =
    process.platform === 'darwin'
      ? 'open'
      : process.platform === 'win32'
        ? 'start'
        : 'xdg-open'
  try {
    execSync(`${opener} "${reportPath}"`, { stdio: 'ignore' })
  } catch {
    console.log(`Could not open browser. Open manually: ${reportPath}`)
  }
}

console.log('\x1b[32mDone!\x1b[0m')
console.log('')
console.log('Tips:')
console.log('  - Open the HTML report to see interactive treemap')
console.log('  - Use --list-modules to see all bundled modules')
console.log('  - Use --list-externals to see external requires')
console.log('  - Use --verbose for detailed build output')
Quest for Codev2.0.0
/
SIGN IN