Find the cost of adding an npm package to your app's bundle size teardown.kelinci.dev
14
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: improve size stat display

Mary 9571ada4 3b809f38

+43 -34
+5 -25
src/components/package-bundle.tsx
··· 1 1 import { createSignal, For, Match, onCleanup, Show, Switch } from 'solid-js'; 2 2 3 3 import { LucideCheck, LucideCircleAlert, LucideInfo, LucideLoader } from '../icons/lucide'; 4 - import { formatBytes } from '../lib/format'; 5 4 import { LRUCache } from '../lib/lru'; 5 + import SizeStat from './size-stat'; 6 6 import { createQuery } from '../lib/query'; 7 7 import { createDerivedSignal } from '../lib/signals'; 8 8 import { progress } from '../npm/events'; ··· 189 189 <div class="flex flex-col gap-5"> 190 190 {/* size display card */} 191 191 <div class="flex items-stretch gap-6 rounded-lg border border-neutral-stroke-3 bg-neutral-background-1 p-4"> 192 - <div class="flex flex-col gap-1"> 193 - <span class="text-base-300 text-neutral-foreground-3">Minified</span> 194 - <span class="text-base-500 font-semibold text-neutral-foreground-1"> 195 - {formatBytes(bundleData().size)} 196 - </span> 197 - </div> 192 + <SizeStat label="Minified" size={bundleData().size} /> 198 193 <div class="w-px bg-neutral-stroke-3" /> 199 - <div class="flex flex-col gap-1"> 200 - <span class="text-base-300 text-neutral-foreground-3">Gzipped</span> 201 - <span class="text-base-500 font-semibold text-neutral-foreground-1"> 202 - {formatBytes(bundleData().gzipSize)} 203 - </span> 204 - </div> 194 + <SizeStat label="Gzip" size={bundleData().gzipSize} /> 205 195 206 196 <Show when={bundleData().brotliSize !== undefined}> 207 197 <div class="w-px bg-neutral-stroke-3" /> 208 - <div class="flex flex-col gap-1"> 209 - <span class="text-base-300 text-neutral-foreground-3">Brotli</span> 210 - <span class="text-base-500 font-semibold text-neutral-foreground-1"> 211 - {formatBytes(bundleData().brotliSize!)} 212 - </span> 213 - </div> 198 + <SizeStat label="Brotli" size={bundleData().brotliSize!} /> 214 199 </Show> 215 200 216 201 <Show when={bundleData().zstdSize !== undefined}> 217 202 <div class="w-px bg-neutral-stroke-3" /> 218 - <div class="flex flex-col gap-1"> 219 - <span class="text-base-300 text-neutral-foreground-3">Zstd</span> 220 - <span class="text-base-500 font-semibold text-neutral-foreground-1"> 221 - {formatBytes(bundleData().zstdSize!)} 222 - </span> 223 - </div> 203 + <SizeStat label="Zstd" size={bundleData().zstdSize!} /> 224 204 </Show> 225 205 <Show when={bundle.state === 'refreshing'}> 226 206 <div class="flex items-center">
+22
src/components/size-stat.tsx
··· 1 + import { formatBytes, formatLongBytes } from '../lib/format'; 2 + import Tooltip from '../primitives/tooltip'; 3 + 4 + interface SizeStatProps { 5 + label: string; 6 + size: number; 7 + } 8 + 9 + const SizeStat = (props: SizeStatProps) => { 10 + return ( 11 + <Tooltip content={formatLongBytes(props.size)} relationship="description"> 12 + {(triggerProps) => ( 13 + <div class="flex flex-col gap-1" {...triggerProps}> 14 + <span class="text-base-300 text-neutral-foreground-3">{props.label}</span> 15 + <span class="text-base-500 font-semibold text-neutral-foreground-1">{formatBytes(props.size)}</span> 16 + </div> 17 + )} 18 + </Tooltip> 19 + ); 20 + }; 21 + 22 + export default SizeStat;
+16 -9
src/lib/format.ts
··· 1 + const byteUnits = ['B', 'kB', 'MB', 'GB'] as const; 2 + 1 3 /** 2 4 * formats bytes into a human-readable string. 3 5 * 4 6 * @param bytes the number of bytes 5 - * @param decimals number of decimal places (default: 1) 6 7 * @returns formatted string like "1.2 kB" or "3.4 MB" 7 8 */ 8 - export function formatBytes(bytes: number, decimals: number = 1): string { 9 - if (bytes === 0) { 10 - return '0 B'; 11 - } 9 + export function formatBytes(bytes: number): string { 10 + const k = 1000; 11 + const i = bytes === 0 ? 0 : Math.min(Math.floor(Math.log(bytes) / Math.log(k)), byteUnits.length - 1); 12 + const value = bytes / Math.pow(k, i); 12 13 13 - const k = 1000; 14 - const sizes = ['B', 'kB', 'MB', 'GB']; 15 - const i = Math.floor(Math.log(bytes) / Math.log(k)); 14 + return `${value.toLocaleString('en-US', { maximumSignificantDigits: 3 })} ${byteUnits[i]}`; 15 + } 16 16 17 - return `${parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))} ${sizes[i]}`; 17 + /** 18 + * formats bytes without unit conversion (always in bytes). 19 + * 20 + * @param bytes the number of bytes 21 + * @returns formatted string like "1,234 B" 22 + */ 23 + export function formatLongBytes(bytes: number): string { 24 + return `${bytes.toLocaleString('en-US')} B`; 18 25 } 19 26 20 27 /**