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.

refactor: enable noUncheckedIndexedAccess

Mary 519f26a7 f8888bd8

+71 -68
+12 -12
src/components/package-dependencies.tsx
··· 106 106 return []; 107 107 } 108 108 if (numSegments === 1) { 109 - return [canonicalIndices[0]]; 109 + return [canonicalIndices[0]!]; 110 110 } 111 111 112 112 // fall back to greedy for very large inputs 113 113 if (numSegments > COLOR_RESOLUTION_DP_LIMIT) { 114 - const resolved: number[] = [canonicalIndices[0]]; 114 + const resolved: number[] = [canonicalIndices[0]!]; 115 115 for (let i = 1; i < numSegments; i++) { 116 - const canonical = canonicalIndices[i]; 116 + const canonical = canonicalIndices[i]!; 117 117 resolved.push(canonical === resolved[i - 1] ? (canonical + 1) % numColors : canonical); 118 118 } 119 119 return resolved; ··· 128 128 129 129 // base case: position 0 130 130 for (let c = 0; c < numColors; c++) { 131 - prev[c] = c === canonicalIndices[0] ? 0 : 1; 131 + prev[c] = c === canonicalIndices[0]! ? 0 : 1; 132 132 } 133 133 134 134 // fill DP table 135 135 for (let i = 1; i < numSegments; i++) { 136 - const canonical = canonicalIndices[i]; 136 + const canonical = canonicalIndices[i]!; 137 137 const parentRow = Array.from({ length: numColors }, () => 0); 138 138 139 139 for (let c = 0; c < numColors; c++) { ··· 143 143 let bestPrevCost = Infinity; 144 144 let bestPrevColor = 0; 145 145 for (let cp = 0; cp < numColors; cp++) { 146 - if (cp !== c && prev[cp] < bestPrevCost) { 147 - bestPrevCost = prev[cp]; 146 + if (cp !== c && prev[cp]! < bestPrevCost) { 147 + bestPrevCost = prev[cp]!; 148 148 bestPrevColor = cp; 149 149 } 150 150 } 151 151 152 - curr[c] = cost + bestPrevCost; 153 - parentRow[c] = bestPrevColor; 152 + curr[c]! = cost + bestPrevCost; 153 + parentRow[c]! = bestPrevColor; 154 154 } 155 155 156 156 parent.push(parentRow); ··· 160 160 // find best final color 161 161 let bestFinal = 0; 162 162 for (let c = 1; c < numColors; c++) { 163 - if (prev[c] < prev[bestFinal]) { 163 + if (prev[c]! < prev[bestFinal]!) { 164 164 bestFinal = c; 165 165 } 166 166 } ··· 169 169 let color = bestFinal; 170 170 const reversed = [color]; 171 171 for (let i = parent.length - 1; i >= 0; i--) { 172 - color = parent[i][color]; 172 + color = parent[i]![color]!; 173 173 reversed.push(color); 174 174 } 175 175 return reversed.reverse(); ··· 192 192 return sorted.map((pkg, i) => ({ 193 193 pkg, 194 194 percent: (pkg.size / props.installSize) * 100, 195 - color: SEGMENT_COLORS[resolvedIndices[i]], 195 + color: SEGMENT_COLORS[resolvedIndices[i]!]!, 196 196 })); 197 197 }); 198 198
+1 -1
src/components/package-search-input.tsx
··· 184 184 ev.preventDefault(); 185 185 const idx = activeIndex(); 186 186 if (idx >= 0 && idx < items.length) { 187 - handleSelect(items[idx]); 187 + handleSelect(items[idx]!); 188 188 } else { 189 189 const parsed = parsePackageSpecifier(props.value.trim()); 190 190 if (parsed) {
+1 -1
src/lib/emitter.ts
··· 87 87 listener.apply(this, args); 88 88 } else { 89 89 for (let idx = 0, len = listener.length; idx < len; idx++) { 90 - listener[idx].apply(this, args); 90 + listener[idx]!.apply(this, args); 91 91 } 92 92 } 93 93 },
+1 -1
src/lib/package-name.ts
··· 25 25 } 26 26 return { 27 27 registry: (match[1] as Registry) ?? 'npm', 28 - name: match[2], 28 + name: match[2]!, 29 29 range: match[3] ?? 'latest', 30 30 }; 31 31 };
+3 -3
src/lib/use-search-params.ts
··· 45 45 const result: Record<string, unknown> = {}; 46 46 47 47 for (const key in definition) { 48 - const schema = definition[key]; 48 + const schema = definition[key]!; 49 49 50 50 let raw: string | string[] | undefined; 51 51 if (schema.type === 'array') { ··· 58 58 if (raw === undefined) { 59 59 result[key] = undefined; 60 60 } else { 61 - const parsed = v.safeParse(schema, raw); 61 + const parsed = v.safeParse(schema!, raw); 62 62 result[key] = parsed.success ? parsed.output : undefined; 63 63 } 64 64 } ··· 91 91 continue; 92 92 } 93 93 94 - const parsed = v.safeParse(definition[key], value); 94 + const parsed = v.safeParse(definition[key]!, value); 95 95 if (!parsed.success) { 96 96 result[key] = undefined; 97 97 continue;
+1 -1
src/npm/lib/fetch.ts
··· 182 182 break; 183 183 } 184 184 185 - const { node, basePath } = queue[i]; 185 + const { node, basePath } = queue[i]!; 186 186 const packagePath = `${basePath}/${node.name}`; 187 187 188 188 const extractedSize = await fetchTarballToVolume(node.tarball, packagePath, volume, exclude);
+9 -9
src/npm/lib/installed-packages.test.ts
··· 6 6 describe('buildInstalledPackages', () => { 7 7 it('builds packages from a simple dependency tree', async () => { 8 8 const result = await resolve(['is-odd@3.0.1']); 9 - const packages = buildInstalledPackages(result.roots[0], new Set()); 9 + const packages = buildInstalledPackages(result.roots[0]!, new Set()); 10 10 11 11 // should have is-odd and is-number 12 12 const names = packages.map((p) => p.name); ··· 25 25 26 26 it('correctly sets dependents', async () => { 27 27 const result = await resolve(['is-odd@3.0.1']); 28 - const packages = buildInstalledPackages(result.roots[0], new Set()); 28 + const packages = buildInstalledPackages(result.roots[0]!, new Set()); 29 29 30 30 // is-odd is the root, no dependents 31 31 const isOdd = packages.find((p) => p.name === 'is-odd')!; ··· 34 34 // is-number is depended on by is-odd 35 35 const isNumber = packages.find((p) => p.name === 'is-number')!; 36 36 expect(isNumber.dependents.length).toBe(1); 37 - expect(isNumber.dependents[0].name).toBe('is-odd'); 37 + expect(isNumber.dependents[0]!.name).toBe('is-odd'); 38 38 }); 39 39 40 40 it('correctly sets dependencies', async () => { 41 41 const result = await resolve(['is-odd@3.0.1']); 42 - const packages = buildInstalledPackages(result.roots[0], new Set()); 42 + const packages = buildInstalledPackages(result.roots[0]!, new Set()); 43 43 44 44 // is-odd has 1 dependency (is-number) 45 45 const isOdd = packages.find((p) => p.name === 'is-odd')!; 46 46 expect(isOdd.dependencies.length).toBe(1); 47 - expect(isOdd.dependencies[0].name).toBe('is-number'); 47 + expect(isOdd.dependencies[0]!.name).toBe('is-number'); 48 48 }); 49 49 50 50 it('marks peer dependencies correctly', async () => { 51 51 // use-sync-external-store has react as a peer dependency 52 52 const result = await resolve(['use-sync-external-store@1.2.0']); 53 53 const peerDepNames = new Set(['react']); 54 - const packages = buildInstalledPackages(result.roots[0], peerDepNames); 54 + const packages = buildInstalledPackages(result.roots[0]!, peerDepNames); 55 55 56 56 // react and its deps should be marked as peer 57 57 const react = packages.find((p) => p.name === 'react'); ··· 74 74 // loose-envify should be marked as peer (only reachable through react) 75 75 const result = await resolve(['use-sync-external-store@1.2.0']); 76 76 const peerDepNames = new Set(['react']); 77 - const packages = buildInstalledPackages(result.roots[0], peerDepNames); 77 + const packages = buildInstalledPackages(result.roots[0]!, peerDepNames); 78 78 79 79 const looseEnvify = packages.find((p) => p.name === 'loose-envify'); 80 80 // loose-envify is a dep of react, which is peer-only ··· 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 - const packages = buildInstalledPackages(result.roots[0], peerDepNames); 93 + const packages = buildInstalledPackages(result.roots[0]!, peerDepNames); 94 94 95 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')!; ··· 103 103 // graphql should still be marked as peer since it's a direct peer dep of root 104 104 const result = await resolve(['graphql-request@7.4.0']); 105 105 const peerDepNames = new Set(['graphql']); 106 - const packages = buildInstalledPackages(result.roots[0], peerDepNames); 106 + const packages = buildInstalledPackages(result.roots[0]!, peerDepNames); 107 107 108 108 // graphql should be marked as peer 109 109 const graphql = packages.find((p) => p.name === 'graphql');
+2 -2
src/npm/lib/module-type.ts
··· 174 174 if (prop.type === 'Identifier' && prop.name === 'defineProperty') { 175 175 const args = expr.arguments; 176 176 if (args.length >= 2) { 177 - const target = args[0]; 178 - const propArg = args[1]; 177 + const target = args[0]!; 178 + const propArg = args[1]!; 179 179 180 180 if ( 181 181 target.type !== 'SpreadElement' &&
+14 -14
src/npm/lib/resolve.test.ts
··· 415 415 const result = await resolve(['is-odd@3.0.1']); 416 416 417 417 expect(result.roots).toHaveLength(1); 418 - expect(result.roots[0].name).toBe('is-odd'); 419 - expect(result.roots[0].version).toBe('3.0.1'); 420 - expect(result.roots[0].dependencies.has('is-number')).toBe(true); 418 + expect(result.roots[0]!.name).toBe('is-odd'); 419 + expect(result.roots[0]!.version).toBe('3.0.1'); 420 + expect(result.roots[0]!.dependencies.has('is-number')).toBe(true); 421 421 }); 422 422 423 423 it('resolves multiple packages', async () => { 424 424 const result = await resolve(['is-odd@3.0.1', 'is-even@1.0.0']); 425 425 426 426 expect(result.roots).toHaveLength(2); 427 - expect(result.roots[0].name).toBe('is-odd'); 428 - expect(result.roots[1].name).toBe('is-even'); 427 + expect(result.roots[0]!.name).toBe('is-odd'); 428 + expect(result.roots[1]!.name).toBe('is-even'); 429 429 }); 430 430 431 431 it('deduplicates shared dependencies', async () => { ··· 446 446 const result = await resolve(['jsr:@luca/flag@1.0.1']); 447 447 448 448 expect(result.roots).toHaveLength(1); 449 - expect(result.roots[0].name).toBe('@luca/flag'); 450 - expect(result.roots[0].version).toBe('1.0.1'); 451 - expect(result.roots[0].tarball).toContain('npm.jsr.io'); 449 + expect(result.roots[0]!.name).toBe('@luca/flag'); 450 + expect(result.roots[0]!.version).toBe('1.0.1'); 451 + expect(result.roots[0]!.tarball).toContain('npm.jsr.io'); 452 452 }); 453 453 454 454 it('resolves JSR package with JSR dependencies', async () => { 455 455 const result = await resolve(['jsr:@std/path@1.1.4']); 456 456 457 - expect(result.roots[0].name).toBe('@std/path'); 457 + expect(result.roots[0]!.name).toBe('@std/path'); 458 458 // dependency stored under npm-compatible name, resolved to canonical 459 - expect(result.roots[0].dependencies.has('@jsr/std__internal')).toBe(true); 460 - const internal = result.roots[0].dependencies.get('@jsr/std__internal')!; 459 + expect(result.roots[0]!.dependencies.has('@jsr/std__internal')).toBe(true); 460 + const internal = result.roots[0]!.dependencies.get('@jsr/std__internal')!; 461 461 expect(internal.name).toBe('@std/internal'); 462 462 expect(internal.tarball).toContain('npm.jsr.io'); 463 463 }); ··· 467 467 it('auto-installs required peer dependencies', async () => { 468 468 const result = await resolve(['use-sync-external-store@1.2.0']); 469 469 470 - const mainPkg = result.roots[0]; 470 + const mainPkg = result.roots[0]!; 471 471 expect(mainPkg.dependencies.has('react')).toBe(true); 472 472 expect(Array.from(result.packages.values()).some((p) => p.name === 'react')).toBe(true); 473 473 }); ··· 476 476 const result = await resolve(['use-sync-external-store@1.2.0']); 477 477 478 478 // react is required, should be present 479 - const mainPkg = result.roots[0]; 479 + const mainPkg = result.roots[0]!; 480 480 expect(mainPkg.dependencies.has('react')).toBe(true); 481 481 }); 482 482 ··· 484 484 const result = await resolve(['use-sync-external-store@1.2.0'], { installPeers: false }); 485 485 486 486 expect(result.roots).toHaveLength(1); 487 - expect(result.roots[0].name).toBe('use-sync-external-store'); 487 + expect(result.roots[0]!.name).toBe('use-sync-external-store'); 488 488 }); 489 489 }); 490 490 });
+5 -5
src/npm/lib/resolve.ts
··· 85 85 ): AbbreviatedManifest | null { 86 86 // empty range means latest 87 87 if (range === '') { 88 - return versions[distTags.latest] ?? null; 88 + return versions[distTags.latest!] ?? null; 89 89 } 90 90 91 91 // check if range is a dist-tag 92 92 if (range in distTags) { 93 - const taggedVersion = distTags[range]; 93 + const taggedVersion = distTags[range]!; 94 94 return versions[taggedVersion] ?? null; 95 95 } 96 96 ··· 131 131 } 132 132 133 133 // prefer non-deprecated versions (pnpm behavior) 134 - const nonDeprecated = validVersions.find((v) => !versions[v].deprecated); 134 + const nonDeprecated = validVersions.find((v) => !versions[v]!.deprecated); 135 135 if (nonDeprecated !== undefined) { 136 - return versions[nonDeprecated]; 136 + return versions[nonDeprecated]!; 137 137 } 138 138 139 139 // fall back to deprecated if no alternatives 140 - return versions[validVersions[0]]; 140 + return versions[validVersions[0]!]!; 141 141 } 142 142 143 143 /**
+5 -3
src/npm/lib/subpaths.ts
··· 115 115 return entries; 116 116 } 117 117 118 - const [prefix, suffix] = targetParts; 118 + const prefix = targetParts[0]!; 119 + const suffix = targetParts[1]!; 119 120 const subpathParts = subpath.split('*'); 120 121 if (subpathParts.length !== 2) { 121 122 return entries; 122 123 } 123 124 124 - const [subpathPrefix, subpathSuffix] = subpathParts; 125 + const subpathPrefix = subpathParts[0]!; 126 + const subpathSuffix = subpathParts[1]!; 125 127 126 128 // normalize the prefix to match volume paths 127 129 // target like "./src/*.js" becomes "/node_modules/pkg/src" ··· 270 272 } else if (entries.length > 0) { 271 273 // otherwise, pick first alphabetically 272 274 entries.sort((a, b) => a.subpath.localeCompare(b.subpath)); 273 - defaultSubpath = entries[0].subpath; 275 + defaultSubpath = entries[0]!.subpath; 274 276 } 275 277 276 278 return {
+2 -2
src/npm/lib/worker-entry.ts
··· 58 58 59 59 await fetchPackagesToVolume(hoisted, volume, options.fetch); 60 60 61 - const mainPackage = resolution.roots[0]; 61 + const mainPackage = resolution.roots[0]!; 62 62 const pkgJsonPath = `/node_modules/${mainPackage.name}/package.json`; 63 63 const pkgJsonContent = volume.readFileSync(pkgJsonPath, 'utf8') as string; 64 64 const manifest = JSON.parse(pkgJsonContent) as PackageJson; ··· 71 71 const peerDependencies = Object.keys(manifest.peerDependencies ?? {}); 72 72 const peerDepNames = new Set(peerDependencies); 73 73 74 - const packages = buildInstalledPackages(mainPackage, peerDepNames); 74 + const packages = buildInstalledPackages(mainPackage!, peerDepNames); 75 75 const installSize = packages.reduce((sum, pkg) => sum + pkg.size, 0); 76 76 77 77 initResult = {
+5 -5
src/primitives/lib/create-active-descendant.ts
··· 82 82 if (enabled.length === 0) { 83 83 return null; 84 84 } 85 - const id = enabled[0].id; 85 + const id = enabled[0]!.id; 86 86 setActiveId(id); 87 87 return id; 88 88 }; ··· 92 92 if (enabled.length === 0) { 93 93 return null; 94 94 } 95 - const id = enabled[enabled.length - 1].id; 95 + const id = enabled[enabled.length - 1]!.id; 96 96 setActiveId(id); 97 97 return id; 98 98 }; ··· 106 106 const currentIndex = getActiveIndex(); 107 107 // circular: wrap to first if at end or no current 108 108 const nextIndex = currentIndex === -1 || currentIndex >= enabled.length - 1 ? 0 : currentIndex + 1; 109 - const id = enabled[nextIndex].id; 109 + const id = enabled[nextIndex]!.id; 110 110 setActiveId(id); 111 111 return id; 112 112 }; ··· 120 120 const currentIndex = getActiveIndex(); 121 121 // circular: wrap to last if at start or no current 122 122 const prevIndex = currentIndex <= 0 ? enabled.length - 1 : currentIndex - 1; 123 - const id = enabled[prevIndex].id; 123 + const id = enabled[prevIndex]!.id; 124 124 setActiveId(id); 125 125 return id; 126 126 }; ··· 146 146 147 147 for (let i = 0; i < enabled.length; i++) { 148 148 const index = (startIndex + i) % enabled.length; 149 - const item = enabled[index]; 149 + const item = enabled[index]!; 150 150 const textValue = item.textValue?.toLowerCase() ?? ''; 151 151 152 152 if (textValue.startsWith(searchBuffer)) {
+8 -8
src/primitives/lib/create-roving-tabindex.ts
··· 67 67 options?.onFocusChange?.(index); 68 68 69 69 if (focus && index >= 0 && index < items.length) { 70 - items[index].el.focus(); 70 + items[index]!.el.focus(); 71 71 } 72 72 }; 73 73 74 74 const getEnabledIndices = (): number[] => { 75 75 const indices: number[] = []; 76 76 for (let i = 0; i < items.length; i++) { 77 - if (!items[i].disabled) { 77 + if (!items[i]!.disabled) { 78 78 indices.push(i); 79 79 } 80 80 } ··· 86 86 if (enabled.length === 0) { 87 87 return; 88 88 } 89 - setFocusedIndex(enabled[0]); 89 + setFocusedIndex(enabled[0]!); 90 90 }; 91 91 92 92 const last = () => { ··· 94 94 if (enabled.length === 0) { 95 95 return; 96 96 } 97 - setFocusedIndex(enabled[enabled.length - 1]); 97 + setFocusedIndex(enabled[enabled.length - 1]!); 98 98 }; 99 99 100 100 const next = () => { ··· 108 108 const currentPos = enabled.indexOf(current); 109 109 // circular: wrap to first if at end or not found 110 110 const nextPos = currentPos === -1 || currentPos >= enabled.length - 1 ? 0 : currentPos + 1; 111 - setFocusedIndex(enabled[nextPos]); 111 + setFocusedIndex(enabled[nextPos]!); 112 112 }; 113 113 114 114 const prev = () => { ··· 122 122 const currentPos = enabled.indexOf(current); 123 123 // circular: wrap to last if at start or not found 124 124 const prevPos = currentPos <= 0 ? enabled.length - 1 : currentPos - 1; 125 - setFocusedIndex(enabled[prevPos]); 125 + setFocusedIndex(enabled[prevPos]!); 126 126 }; 127 127 128 128 const search = (char: string) => { ··· 147 147 148 148 for (let i = 0; i < enabled.length; i++) { 149 149 const pos = (startPos + i) % enabled.length; 150 - const index = enabled[pos]; 151 - const item = items[index]; 150 + const index = enabled[pos]!; 151 + const item = items[index]!; 152 152 const textValue = item.textValue?.toLowerCase() ?? item.el.textContent?.toLowerCase() ?? ''; 153 153 154 154 if (textValue.startsWith(searchBuffer)) {
+2 -1
tsconfig.app.json
··· 23 23 "noUnusedParameters": true, 24 24 "erasableSyntaxOnly": true, 25 25 "noFallthroughCasesInSwitch": true, 26 - "noUncheckedSideEffectImports": true 26 + "noUncheckedSideEffectImports": true, 27 + "noUncheckedIndexedAccess": true 27 28 }, 28 29 "include": ["src"] 29 30 }