usage-size.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // @ts-check
  2. import { mkdir, writeFile } from 'node:fs/promises'
  3. import path from 'node:path'
  4. import { rollup } from 'rollup'
  5. import nodeResolve from '@rollup/plugin-node-resolve'
  6. import { minify } from '@swc/core'
  7. import replace from '@rollup/plugin-replace'
  8. import { brotliCompressSync, gzipSync } from 'node:zlib'
  9. import { parseArgs } from 'node:util'
  10. import pico from 'picocolors'
  11. import prettyBytes from 'pretty-bytes'
  12. const {
  13. values: { write },
  14. } = parseArgs({
  15. options: {
  16. write: {
  17. type: 'boolean',
  18. default: false,
  19. },
  20. },
  21. })
  22. const sizeDir = path.resolve('temp/size')
  23. const vue = path.resolve('./packages/vue/dist/vue.runtime.esm-bundler.js')
  24. const vapor = path.resolve(
  25. './packages/vue-vapor/dist/vue-vapor.runtime.esm-bundler.js',
  26. )
  27. /**
  28. * @typedef {Object} Preset
  29. * @property {string} name - The name of the preset
  30. * @property {'*' | string[]} imports - The imports that are part of this preset
  31. * @property {string} from - The path to the entry file
  32. * @property {Record<string, string>} [replace]
  33. */
  34. /** @type {Preset[]} */
  35. const presets = [
  36. {
  37. name: 'createApp (CAPI only)',
  38. imports: ['createApp'],
  39. replace: { __VUE_OPTIONS_API__: 'false' },
  40. from: vue,
  41. },
  42. { name: 'createApp', imports: ['createApp'], from: vue },
  43. { name: 'createSSRApp', imports: ['createSSRApp'], from: vue },
  44. { name: 'defineCustomElement', imports: ['defineCustomElement'], from: vue },
  45. { name: 'vapor', imports: '*', from: vapor },
  46. {
  47. name: 'overall',
  48. imports: [
  49. 'createApp',
  50. 'ref',
  51. 'watch',
  52. 'Transition',
  53. 'KeepAlive',
  54. 'Suspense',
  55. ],
  56. from: vue,
  57. },
  58. ]
  59. main()
  60. /**
  61. * Main function that initiates the bundling process for the presets
  62. */
  63. async function main() {
  64. console.log()
  65. /** @type {Promise<{name: string, size: number, gzip: number, brotli: number}>[]} */
  66. const tasks = []
  67. for (const preset of presets) {
  68. tasks.push(generateBundle(preset))
  69. }
  70. const results = await Promise.all(tasks)
  71. for (const r of results) {
  72. console.log(
  73. `${pico.green(pico.bold(r.name))} - ` +
  74. `min:${prettyBytes(r.size, { minimumFractionDigits: 3 })} / ` +
  75. `gzip:${prettyBytes(r.gzip, { minimumFractionDigits: 3 })} / ` +
  76. `brotli:${prettyBytes(r.brotli, { minimumFractionDigits: 3 })}`,
  77. )
  78. }
  79. await mkdir(sizeDir, { recursive: true })
  80. await writeFile(
  81. path.resolve(sizeDir, '_usages.json'),
  82. JSON.stringify(Object.fromEntries(results.map(r => [r.name, r])), null, 2),
  83. 'utf-8',
  84. )
  85. }
  86. /**
  87. * Generates a bundle for a given preset
  88. *
  89. * @param {Preset} preset - The preset to generate the bundle for
  90. * @returns {Promise<{name: string, size: number, gzip: number, brotli: number}>} - The result of the bundling process
  91. */
  92. async function generateBundle(preset) {
  93. const id = 'virtual:entry'
  94. const exportSpecifiers =
  95. preset.imports === '*'
  96. ? `* as ${preset.name}`
  97. : `{ ${preset.imports.join(', ')} }`
  98. const content = `export ${exportSpecifiers} from '${preset.from}'`
  99. const result = await rollup({
  100. input: id,
  101. plugins: [
  102. {
  103. name: 'usage-size-plugin',
  104. resolveId(_id) {
  105. if (_id === id) return id
  106. return null
  107. },
  108. load(_id) {
  109. if (_id === id) return content
  110. },
  111. },
  112. nodeResolve(),
  113. replace({
  114. 'process.env.NODE_ENV': '"production"',
  115. __VUE_PROD_DEVTOOLS__: 'false',
  116. __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false',
  117. __VUE_OPTIONS_API__: 'true',
  118. preventAssignment: true,
  119. ...preset.replace,
  120. }),
  121. ],
  122. })
  123. const generated = await result.generate({})
  124. const bundled = generated.output[0].code
  125. const minified = (
  126. await minify(bundled, {
  127. module: true,
  128. toplevel: true,
  129. })
  130. ).code
  131. const size = minified.length
  132. const gzip = gzipSync(minified).length
  133. const brotli = brotliCompressSync(minified).length
  134. if (write) {
  135. await writeFile(path.resolve(sizeDir, preset.name + '.js'), bundled)
  136. }
  137. return {
  138. name: preset.name,
  139. size,
  140. gzip,
  141. brotli,
  142. }
  143. }