size-report.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // @ts-check
  2. import path from 'node:path'
  3. import { markdownTable } from 'markdown-table'
  4. import prettyBytes from 'pretty-bytes'
  5. import { readdir } from 'node:fs/promises'
  6. import { existsSync } from 'node:fs'
  7. /**
  8. * @typedef {Object} SizeResult
  9. * @property {number} size
  10. * @property {number} gzip
  11. * @property {number} brotli
  12. */
  13. /**
  14. * @typedef {SizeResult & { file: string }} BundleResult
  15. */
  16. /**
  17. * @typedef {Record<string, SizeResult & { name: string }>} UsageResult
  18. */
  19. const currDir = path.resolve('temp/size')
  20. const prevDir = path.resolve('temp/size-prev')
  21. let output = '## Size Report\n\n'
  22. const sizeHeaders = ['Size', 'Gzip', 'Brotli']
  23. run()
  24. /**
  25. * Runs the main process of rendering file and usage data
  26. */
  27. async function run() {
  28. await renderFiles()
  29. await renderUsages()
  30. process.stdout.write(output)
  31. }
  32. /**
  33. * Renders file sizes and diffs between current and previous versions
  34. */
  35. async function renderFiles() {
  36. const filterFiles = files =>
  37. files.filter(file => file[0] !== '_' && !file.endsWith('.txt'))
  38. const curr = filterFiles(await readdir(currDir))
  39. const prev = existsSync(prevDir) ? filterFiles(await readdir(prevDir)) : []
  40. const fileList = new Set([...curr, ...prev])
  41. const rows = []
  42. for (const file of fileList) {
  43. const currPath = path.resolve(currDir, file)
  44. const prevPath = path.resolve(prevDir, file)
  45. const curr = await importJSON(currPath)
  46. const prev = await importJSON(prevPath)
  47. const fileName = curr?.file || prev?.file || ''
  48. if (!curr) {
  49. rows.push([`~~${fileName}~~`])
  50. } else {
  51. rows.push([
  52. fileName,
  53. `${prettyBytes(curr.size)}${getDiff(curr.size, prev?.size)}`,
  54. `${prettyBytes(curr.gzip)}${getDiff(curr.gzip, prev?.gzip)}`,
  55. `${prettyBytes(curr.brotli)}${getDiff(curr.brotli, prev?.brotli)}`,
  56. ])
  57. }
  58. }
  59. output += '### Bundles\n\n'
  60. output += markdownTable([['File', ...sizeHeaders], ...rows])
  61. output += '\n\n'
  62. }
  63. /**
  64. * Renders usage data comparing current and previous usage results
  65. */
  66. async function renderUsages() {
  67. const curr = await importJSON(path.resolve(currDir, '_usages.json'))
  68. const prev = await importJSON(path.resolve(prevDir, '_usages.json'))
  69. output += '\n### Usages\n\n'
  70. const data = Object.values(curr)
  71. .map(usage => {
  72. const prevUsage = prev?.[usage.name]
  73. const diffSize = getDiff(usage.size, prevUsage?.size)
  74. const diffGzipped = getDiff(usage.gzip, prevUsage?.gzip)
  75. const diffBrotli = getDiff(usage.brotli, prevUsage?.brotli)
  76. return [
  77. usage.name,
  78. `${prettyBytes(usage.size)}${diffSize}`,
  79. `${prettyBytes(usage.gzip)}${diffGzipped}`,
  80. `${prettyBytes(usage.brotli)}${diffBrotli}`,
  81. ]
  82. })
  83. .filter(usage => !!usage)
  84. output += `${markdownTable([['Name', ...sizeHeaders], ...data])}\n\n`
  85. }
  86. /**
  87. * Imports JSON data from a specified path
  88. *
  89. * @template T
  90. * @param {string} filePath - Path to the JSON file
  91. * @returns {Promise<T | undefined>} The JSON content or undefined if the file does not exist
  92. */
  93. async function importJSON(filePath) {
  94. if (!existsSync(filePath)) return undefined
  95. return (await import(filePath, { with: { type: 'json' } })).default
  96. }
  97. /**
  98. * Calculates the difference between the current and previous sizes
  99. *
  100. * @param {number} curr - The current size
  101. * @param {number} [prev] - The previous size
  102. * @returns {string} The difference in pretty format
  103. */
  104. function getDiff(curr, prev) {
  105. if (prev === undefined) return ''
  106. const diff = curr - prev
  107. if (diff === 0) return ''
  108. const sign = diff > 0 ? '+' : ''
  109. return ` (**${sign}${prettyBytes(diff)}**)`
  110. }