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.

fix: properly omit peer dependency from install size

Mary 72336a3c 77095984

+35 -11
+5 -6
src/components/package-dependencies.tsx
··· 114 114 115 115 interface PackageCardProps { 116 116 pkg: InstalledPackage; 117 - percent: number; 117 + installSize: number; 118 118 } 119 119 120 120 const PackageCard = (props: PackageCardProps) => { 121 + const percent = () => (props.pkg.size / props.installSize) * 100; 122 + 121 123 return ( 122 124 <div class="group duration-fast flex gap-4 rounded-lg border border-transparent px-3 py-4 transition hover:border-neutral-stroke-3 hover:bg-neutral-background-1"> 123 125 {/* left side: percentage and size */} 124 126 <div class="flex w-16 shrink-0 flex-col items-end text-right"> 125 - <span class="text-base-400 font-semibold text-neutral-foreground-1">{props.percent.toFixed(0)}%</span> 127 + <span class="text-base-400 font-semibold text-neutral-foreground-1">{percent().toFixed(0)}%</span> 126 128 <span class="text-base-300 text-neutral-foreground-3">{formatBytes(props.pkg.size)}</span> 127 129 </div> 128 130 ··· 244 246 {/* package list */} 245 247 <div class="-mx-3 flex flex-col"> 246 248 <For each={filteredAndSorted()}> 247 - {(pkg) => { 248 - const percent = (pkg.size / displayInstallSize()) * 100; 249 - return <PackageCard pkg={pkg} percent={percent} />; 250 - }} 249 + {(pkg) => <PackageCard pkg={pkg} installSize={displayInstallSize()} />} 251 250 </For> 252 251 253 252 <Show when={filteredAndSorted().length === 0}>
+23 -4
src/npm/lib/installed-packages.test.ts
··· 83 83 } 84 84 }); 85 85 86 - it('does not mark shared deps as peer when reachable both ways', async () => { 87 - // if a package is reachable through both regular and peer deps, 88 - // it should NOT be marked as peer 86 + it('marks direct peer deps as peer even when also a transitive dep', async () => { 87 + // if a package is a direct peer dep of root but also reachable through 88 + // a transitive non-peer path, it should still be marked as peer 89 89 const result = await resolve(['is-odd@3.0.1']); 90 90 91 91 // pretend is-number is also a peer dep (but it's already a regular dep) 92 92 const peerDepNames = new Set(['is-number']); 93 93 const packages = buildInstalledPackages(result.roots[0], peerDepNames); 94 94 95 - // is-number should still be marked as peer because it's only through peer edge 95 + // is-number should be marked as peer because it's a direct peer dep of root 96 96 const isNumber = packages.find((p) => p.name === 'is-number')!; 97 97 expect(isNumber.isPeer).toBe(true); 98 + }); 99 + 100 + it('marks peer deps as peer when also reachable through transitive deps', async () => { 101 + // graphql-request has graphql as a peer dep 102 + // @graphql-typed-document-node/core (a regular dep) also depends on graphql 103 + // graphql should still be marked as peer since it's a direct peer dep of root 104 + const result = await resolve(['graphql-request@7.4.0']); 105 + const peerDepNames = new Set(['graphql']); 106 + const packages = buildInstalledPackages(result.roots[0], peerDepNames); 107 + 108 + // graphql should be marked as peer 109 + const graphql = packages.find((p) => p.name === 'graphql'); 110 + expect(graphql).toBeDefined(); 111 + expect(graphql!.isPeer).toBe(true); 112 + 113 + // @graphql-typed-document-node/core should NOT be marked as peer 114 + const typedDocNode = packages.find((p) => p.name === '@graphql-typed-document-node/core'); 115 + expect(typedDocNode).toBeDefined(); 116 + expect(typedDocNode!.isPeer).toBe(false); 98 117 }); 99 118 });
+7 -1
src/npm/lib/installed-packages.ts
··· 101 101 // build final array 102 102 const packages: InstalledPackage[] = []; 103 103 for (const [key, { pkg, level, dependents, dependencies }] of packageMap) { 104 + // a package is a peer if: 105 + // 1. it's a direct peer dependency of the root, OR 106 + // 2. it's only reachable through peer dependency subtrees 107 + const isDirectPeerOfRoot = peerDepNames.has(pkg.name); 108 + const isOnlyReachableThroughPeers = !reachableWithoutPeers.has(key); 109 + 104 110 packages.push({ 105 111 name: pkg.name, 106 112 version: pkg.version, ··· 111 117 dependencies, 112 118 description: pkg.description, 113 119 license: pkg.license, 114 - isPeer: !reachableWithoutPeers.has(key), 120 + isPeer: isDirectPeerOfRoot || isOnlyReachableThroughPeers, 115 121 }); 116 122 } 117 123