diff --git a/.github/workflows/size-report.yml b/.github/workflows/size-report.yml index d8d0c416a..f9d2052b6 100644 --- a/.github/workflows/size-report.yml +++ b/.github/workflows/size-report.yml @@ -66,7 +66,7 @@ jobs: 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 diff --git a/package.json b/package.json index 55d42661e..0a5949940 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "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", @@ -104,7 +104,6 @@ "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:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 886c15f52..a24caa1f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -167,9 +167,6 @@ importers: 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 @@ -3224,11 +3221,6 @@ packages: 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'} @@ -6253,13 +6245,6 @@ snapshots: 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 diff --git a/scripts/size-report.ts b/scripts/size-report.js similarity index 60% rename from scripts/size-report.ts rename to scripts/size-report.js index 363fa9279..f92577d0d 100644 --- a/scripts/size-report.ts +++ b/scripts/size-report.js @@ -1,20 +1,24 @@ +// @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 +/** + * @typedef {Record} UsageResult + */ const currDir = path.resolve('temp/size') const prevDir = path.resolve('temp/size-prev') @@ -23,6 +27,9 @@ const sizeHeaders = ['Size', 'Gzip', 'Brotli'] run() +/** + * Runs the main process of rendering file and usage data + */ async function run() { await renderFiles() await renderUsages() @@ -30,32 +37,36 @@ async function run() { 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(currPath) - const prev = await importJSON(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' @@ -63,13 +74,13 @@ async function renderFiles() { output += '\n\n' } +/** + * Renders usage data comparing current and previous usage results + */ async function renderUsages() { - const curr = (await importJSON( - path.resolve(currDir, '_usages.json'), - ))! - const prev = await importJSON( - 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) @@ -86,17 +97,31 @@ async function renderUsages() { `${prettyBytes(usage.brotli)}${diffBrotli}`, ] }) - .filter((usage): usage is string[] => !!usage) + .filter(usage => !!usage) output += `${markdownTable([['Name', ...sizeHeaders], ...data])}\n\n` } -async function importJSON(path: string): Promise { - 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} 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 '' diff --git a/scripts/usage-size.ts b/scripts/usage-size.js similarity index 64% rename from scripts/usage-size.ts rename to scripts/usage-size.js index 5b915866c..c68e3703b 100644 --- a/scripts/usage-size.ts +++ b/scripts/usage-size.js @@ -1,3 +1,4 @@ +// @ts-check import { mkdir, writeFile } from 'node:fs/promises' import path from 'node:path' import { rollup } from 'rollup' @@ -23,12 +24,20 @@ const { 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} [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'] }, @@ -47,28 +56,45 @@ const presets: Preset[] = [ main() +/** + * Main function that initiates the bundling process for the presets + */ async function main() { console.log() - const tasks: ReturnType[] = [] + /** @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: [ @@ -89,6 +115,7 @@ async function generateBundle(preset: Preset) { __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false', __VUE_OPTIONS_API__: 'true', preventAssignment: true, + ...preset.replace, }), ], }) @@ -100,7 +127,7 @@ async function generateBundle(preset: Preset) { module: true, toplevel: true, }) - ).code! + ).code const size = minified.length const gzip = gzipSync(minified).length @@ -110,13 +137,6 @@ async function generateBundle(preset: Preset) { 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,