if_no_artifact_found: warn
- name: Prepare report
- run: pnpm tsx scripts/size-report.ts > size-report.md
+ run: node scripts/size-report.js > size-report.md
- name: Read Size Report
id: size-report
"build": "node scripts/build.js",
"build-dts": "tsc -p tsconfig.build.json --noCheck && rollup -c rollup.dts.config.js",
"clean": "rimraf --glob packages/*/dist temp .eslintcache",
- "size": "run-s \"size-*\" && tsx scripts/usage-size.ts",
+ "size": "run-s \"size-*\" && node scripts/usage-size.js",
"size-global": "node scripts/build.js vue runtime-dom -f global -p --size",
"size-esm-runtime": "node scripts/build.js vue -f esm-bundler-runtime",
"size-esm": "node scripts/build.js runtime-dom runtime-core reactivity shared -f esm-bundler",
"simple-git-hooks": "^2.11.1",
"todomvc-app-css": "^2.4.3",
"tslib": "^2.7.0",
- "tsx": "^4.19.0",
"typescript": "~5.6.2",
"typescript-eslint": "^8.4.0",
"vite": "catalog:",
tslib:
specifier: ^2.7.0
version: 2.7.0
- tsx:
- specifier: ^4.19.0
- version: 4.19.0
typescript:
specifier: ~5.6.2
version: 5.6.2
tslib@2.7.0:
resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
- tsx@4.19.0:
- resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==}
- engines: {node: '>=18.0.0'}
- hasBin: true
-
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
tslib@2.7.0: {}
- tsx@4.19.0:
- dependencies:
- esbuild: 0.23.1
- get-tsconfig: 4.7.6
- optionalDependencies:
- fsevents: 2.3.3
-
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
+// @ts-check
import path from 'node:path'
import { markdownTable } from 'markdown-table'
import prettyBytes from 'pretty-bytes'
import { readdir } from 'node:fs/promises'
import { existsSync } from 'node:fs'
-interface SizeResult {
- size: number
- gzip: number
- brotli: number
-}
+/**
+ * @typedef {Object} SizeResult
+ * @property {number} size
+ * @property {number} gzip
+ * @property {number} brotli
+ */
-interface BundleResult extends SizeResult {
- file: string
-}
+/**
+ * @typedef {SizeResult & { file: string }} BundleResult
+ */
-type UsageResult = Record<string, SizeResult & { name: string }>
+/**
+ * @typedef {Record<string, SizeResult & { name: string }>} UsageResult
+ */
const currDir = path.resolve('temp/size')
const prevDir = path.resolve('temp/size-prev')
run()
+/**
+ * Runs the main process of rendering file and usage data
+ */
async function run() {
await renderFiles()
await renderUsages()
process.stdout.write(output)
}
+/**
+ * Renders file sizes and diffs between current and previous versions
+ */
async function renderFiles() {
- const filterFiles = (files: string[]) =>
+ const filterFiles = files =>
files.filter(file => file[0] !== '_' && !file.endsWith('.txt'))
const curr = filterFiles(await readdir(currDir))
const prev = existsSync(prevDir) ? filterFiles(await readdir(prevDir)) : []
const fileList = new Set([...curr, ...prev])
- const rows: string[][] = []
+ const rows = []
for (const file of fileList) {
const currPath = path.resolve(currDir, file)
const prevPath = path.resolve(prevDir, file)
- const curr = await importJSON<BundleResult>(currPath)
- const prev = await importJSON<BundleResult>(prevPath)
+ const curr = await importJSON(currPath)
+ const prev = await importJSON(prevPath)
const fileName = curr?.file || prev?.file || ''
if (!curr) {
rows.push([`~~${fileName}~~`])
- } else
+ } else {
rows.push([
fileName,
`${prettyBytes(curr.size)}${getDiff(curr.size, prev?.size)}`,
`${prettyBytes(curr.gzip)}${getDiff(curr.gzip, prev?.gzip)}`,
`${prettyBytes(curr.brotli)}${getDiff(curr.brotli, prev?.brotli)}`,
])
+ }
}
output += '### Bundles\n\n'
output += '\n\n'
}
+/**
+ * Renders usage data comparing current and previous usage results
+ */
async function renderUsages() {
- const curr = (await importJSON<UsageResult>(
- path.resolve(currDir, '_usages.json'),
- ))!
- const prev = await importJSON<UsageResult>(
- path.resolve(prevDir, '_usages.json'),
- )
+ const curr = await importJSON(path.resolve(currDir, '_usages.json'))
+ const prev = await importJSON(path.resolve(prevDir, '_usages.json'))
+
output += '\n### Usages\n\n'
const data = Object.values(curr)
`${prettyBytes(usage.brotli)}${diffBrotli}`,
]
})
- .filter((usage): usage is string[] => !!usage)
+ .filter(usage => !!usage)
output += `${markdownTable([['Name', ...sizeHeaders], ...data])}\n\n`
}
-async function importJSON<T>(path: string): Promise<T | undefined> {
- if (!existsSync(path)) return undefined
- return (await import(path, { assert: { type: 'json' } })).default
+/**
+ * Imports JSON data from a specified path
+ *
+ * @template T
+ * @param {string} filePath - Path to the JSON file
+ * @returns {Promise<T | undefined>} The JSON content or undefined if the file does not exist
+ */
+async function importJSON(filePath) {
+ if (!existsSync(filePath)) return undefined
+ return (await import(filePath, { assert: { type: 'json' } })).default
}
-function getDiff(curr: number, prev?: number) {
+/**
+ * Calculates the difference between the current and previous sizes
+ *
+ * @param {number} curr - The current size
+ * @param {number} [prev] - The previous size
+ * @returns {string} The difference in pretty format
+ */
+function getDiff(curr, prev) {
if (prev === undefined) return ''
const diff = curr - prev
if (diff === 0) return ''
+// @ts-check
import { mkdir, writeFile } from 'node:fs/promises'
import path from 'node:path'
import { rollup } from 'rollup'
const sizeDir = path.resolve('temp/size')
const entry = path.resolve('./packages/vue/dist/vue.runtime.esm-bundler.js')
-interface Preset {
- name: string
- imports: string[]
-}
+/**
+ * @typedef {Object} Preset
+ * @property {string} name - The name of the preset
+ * @property {string[]} imports - The imports that are part of this preset
+ * @property {Record<string, string>} [replace]
+ */
-const presets: Preset[] = [
+/** @type {Preset[]} */
+const presets = [
+ {
+ name: 'createApp (CAPI only)',
+ imports: ['createApp'],
+ replace: { __VUE_OPTIONS_API__: 'false' },
+ },
{ name: 'createApp', imports: ['createApp'] },
{ name: 'createSSRApp', imports: ['createSSRApp'] },
{ name: 'defineCustomElement', imports: ['defineCustomElement'] },
main()
+/**
+ * Main function that initiates the bundling process for the presets
+ */
async function main() {
console.log()
- const tasks: ReturnType<typeof generateBundle>[] = []
+ /** @type {Promise<{name: string, size: number, gzip: number, brotli: number}>[]} */
+ const tasks = []
for (const preset of presets) {
tasks.push(generateBundle(preset))
}
+ const results = await Promise.all(tasks)
- const results = Object.fromEntries(
- (await Promise.all(tasks)).map(r => [r.name, r]),
- )
+ for (const r of results) {
+ console.log(
+ `${pico.green(pico.bold(r.name))} - ` +
+ `min:${prettyBytes(r.size, { minimumFractionDigits: 3 })} / ` +
+ `gzip:${prettyBytes(r.gzip, { minimumFractionDigits: 3 })} / ` +
+ `brotli:${prettyBytes(r.brotli, { minimumFractionDigits: 3 })}`,
+ )
+ }
await mkdir(sizeDir, { recursive: true })
await writeFile(
path.resolve(sizeDir, '_usages.json'),
- JSON.stringify(results, null, 2),
+ JSON.stringify(Object.fromEntries(results.map(r => [r.name, r])), null, 2),
'utf-8',
)
}
-async function generateBundle(preset: Preset) {
+/**
+ * Generates a bundle for a given preset
+ *
+ * @param {Preset} preset - The preset to generate the bundle for
+ * @returns {Promise<{name: string, size: number, gzip: number, brotli: number}>} - The result of the bundling process
+ */
+async function generateBundle(preset) {
const id = 'virtual:entry'
const content = `export { ${preset.imports.join(', ')} } from '${entry}'`
+
const result = await rollup({
input: id,
plugins: [
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false',
__VUE_OPTIONS_API__: 'true',
preventAssignment: true,
+ ...preset.replace,
}),
],
})
module: true,
toplevel: true,
})
- ).code!
+ ).code
const size = minified.length
const gzip = gzipSync(minified).length
await writeFile(path.resolve(sizeDir, preset.name + '.js'), bundled)
}
- console.log(
- `${pico.green(pico.bold(preset.name))} - ` +
- `min:${prettyBytes(size, { minimumFractionDigits: 3 })} / ` +
- `gzip:${prettyBytes(gzip, { minimumFractionDigits: 3 })} / ` +
- `brotli:${prettyBytes(brotli, { minimumFractionDigits: 3 })}`,
- )
-
return {
name: preset.name,
size,