usage-size.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import { mkdir, writeFile } from 'node:fs/promises'
  2. import path from 'node:path'
  3. import { rollup } from 'rollup'
  4. import nodeResolve from '@rollup/plugin-node-resolve'
  5. import { minify } from '@swc/core'
  6. import replace from '@rollup/plugin-replace'
  7. import { brotliCompressSync, gzipSync } from 'node:zlib'
  8. import { parseArgs } from 'node:util'
  9. import pico from 'picocolors'
  10. import prettyBytes from 'pretty-bytes'
  11. const {
  12. values: { write },
  13. } = parseArgs({
  14. options: {
  15. write: {
  16. type: 'boolean',
  17. default: false,
  18. },
  19. },
  20. })
  21. const sizeDir = path.resolve('temp/size')
  22. const entry = path.resolve('./packages/vue/dist/vue.runtime.esm-bundler.js')
  23. interface Preset {
  24. name: string
  25. imports: string[]
  26. }
  27. const presets: Preset[] = [
  28. { name: 'createApp', imports: ['createApp'] },
  29. { name: 'createSSRApp', imports: ['createSSRApp'] },
  30. { name: 'defineCustomElement', imports: ['defineCustomElement'] },
  31. {
  32. name: 'overall',
  33. imports: [
  34. 'createApp',
  35. 'ref',
  36. 'watch',
  37. 'Transition',
  38. 'KeepAlive',
  39. 'Suspense',
  40. ],
  41. },
  42. ]
  43. main()
  44. async function main() {
  45. console.log()
  46. const tasks: ReturnType<typeof generateBundle>[] = []
  47. for (const preset of presets) {
  48. tasks.push(generateBundle(preset))
  49. }
  50. const results = Object.fromEntries(
  51. (await Promise.all(tasks)).map(r => [r.name, r]),
  52. )
  53. await mkdir(sizeDir, { recursive: true })
  54. await writeFile(
  55. path.resolve(sizeDir, '_usages.json'),
  56. JSON.stringify(results, null, 2),
  57. 'utf-8',
  58. )
  59. }
  60. async function generateBundle(preset: Preset) {
  61. const id = 'virtual:entry'
  62. const content = `export { ${preset.imports.join(', ')} } from '${entry}'`
  63. const result = await rollup({
  64. input: id,
  65. plugins: [
  66. {
  67. name: 'usage-size-plugin',
  68. resolveId(_id) {
  69. if (_id === id) return id
  70. return null
  71. },
  72. load(_id) {
  73. if (_id === id) return content
  74. },
  75. },
  76. nodeResolve(),
  77. replace({
  78. 'process.env.NODE_ENV': '"production"',
  79. __VUE_PROD_DEVTOOLS__: 'false',
  80. __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false',
  81. __VUE_OPTIONS_API__: 'true',
  82. preventAssignment: true,
  83. }),
  84. ],
  85. })
  86. const generated = await result.generate({})
  87. const bundled = generated.output[0].code
  88. const minified = (
  89. await minify(bundled, {
  90. module: true,
  91. toplevel: true,
  92. })
  93. ).code!
  94. const size = minified.length
  95. const gzip = gzipSync(minified).length
  96. const brotli = brotliCompressSync(minified).length
  97. if (write) {
  98. await writeFile(path.resolve(sizeDir, preset.name + '.js'), bundled)
  99. }
  100. console.log(
  101. `${pico.green(pico.bold(preset.name))} - ` +
  102. `min:${prettyBytes(size, { minimumFractionDigits: 3 })} / ` +
  103. `gzip:${prettyBytes(gzip, { minimumFractionDigits: 3 })} / ` +
  104. `brotli:${prettyBytes(brotli, { minimumFractionDigits: 3 })}`,
  105. )
  106. return {
  107. name: preset.name,
  108. size,
  109. gzip,
  110. brotli,
  111. }
  112. }