tangled
alpha
login
or
join now
mary.my.id
/
teardown
13
fork
atom
Find the cost of adding an npm package to your app's bundle size
teardown.kelinci.dev
13
fork
atom
overview
issues
pulls
pipelines
refactor: enable noUncheckedIndexedAccess
mary.my.id
1 month ago
519f26a7
f8888bd8
verified
This commit was signed with the committer's
known signature
.
mary.my.id
SSH Key Fingerprint:
SHA256:ZlTP/auFSGpGnaoDg4mCTG1g9OZvXp62jWR4c6H4O3c=
+71
-68
15 changed files
expand all
collapse all
unified
split
src
components
package-dependencies.tsx
package-search-input.tsx
lib
emitter.ts
package-name.ts
use-search-params.ts
npm
lib
fetch.ts
installed-packages.test.ts
module-type.ts
resolve.test.ts
resolve.ts
subpaths.ts
worker-entry.ts
primitives
lib
create-active-descendant.ts
create-roving-tabindex.ts
tsconfig.app.json
+12
-12
src/components/package-dependencies.tsx
···
106
106
return [];
107
107
}
108
108
if (numSegments === 1) {
109
109
-
return [canonicalIndices[0]];
109
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
114
-
const resolved: number[] = [canonicalIndices[0]];
114
114
+
const resolved: number[] = [canonicalIndices[0]!];
115
115
for (let i = 1; i < numSegments; i++) {
116
116
-
const canonical = canonicalIndices[i];
116
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
131
-
prev[c] = c === canonicalIndices[0] ? 0 : 1;
131
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
136
-
const canonical = canonicalIndices[i];
136
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
146
-
if (cp !== c && prev[cp] < bestPrevCost) {
147
147
-
bestPrevCost = prev[cp];
146
146
+
if (cp !== c && prev[cp]! < bestPrevCost) {
147
147
+
bestPrevCost = prev[cp]!;
148
148
bestPrevColor = cp;
149
149
}
150
150
}
151
151
152
152
-
curr[c] = cost + bestPrevCost;
153
153
-
parentRow[c] = bestPrevColor;
152
152
+
curr[c]! = cost + bestPrevCost;
153
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
163
-
if (prev[c] < prev[bestFinal]) {
163
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
172
-
color = parent[i][color];
172
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
195
-
color: SEGMENT_COLORS[resolvedIndices[i]],
195
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
187
-
handleSelect(items[idx]);
187
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
90
-
listener[idx].apply(this, args);
90
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
28
-
name: match[2],
28
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
48
-
const schema = definition[key];
48
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
61
-
const parsed = v.safeParse(schema, raw);
61
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
94
-
const parsed = v.safeParse(definition[key], value);
94
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
185
-
const { node, basePath } = queue[i];
185
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
9
-
const packages = buildInstalledPackages(result.roots[0], new Set());
9
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
28
-
const packages = buildInstalledPackages(result.roots[0], new Set());
28
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
37
-
expect(isNumber.dependents[0].name).toBe('is-odd');
37
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
42
-
const packages = buildInstalledPackages(result.roots[0], new Set());
42
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
47
-
expect(isOdd.dependencies[0].name).toBe('is-number');
47
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
54
-
const packages = buildInstalledPackages(result.roots[0], peerDepNames);
54
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
77
-
const packages = buildInstalledPackages(result.roots[0], peerDepNames);
77
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
93
-
const packages = buildInstalledPackages(result.roots[0], peerDepNames);
93
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
106
-
const packages = buildInstalledPackages(result.roots[0], peerDepNames);
106
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
177
-
const target = args[0];
178
178
-
const propArg = args[1];
177
177
+
const target = args[0]!;
178
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
418
-
expect(result.roots[0].name).toBe('is-odd');
419
419
-
expect(result.roots[0].version).toBe('3.0.1');
420
420
-
expect(result.roots[0].dependencies.has('is-number')).toBe(true);
418
418
+
expect(result.roots[0]!.name).toBe('is-odd');
419
419
+
expect(result.roots[0]!.version).toBe('3.0.1');
420
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
427
-
expect(result.roots[0].name).toBe('is-odd');
428
428
-
expect(result.roots[1].name).toBe('is-even');
427
427
+
expect(result.roots[0]!.name).toBe('is-odd');
428
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
449
-
expect(result.roots[0].name).toBe('@luca/flag');
450
450
-
expect(result.roots[0].version).toBe('1.0.1');
451
451
-
expect(result.roots[0].tarball).toContain('npm.jsr.io');
449
449
+
expect(result.roots[0]!.name).toBe('@luca/flag');
450
450
+
expect(result.roots[0]!.version).toBe('1.0.1');
451
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
457
-
expect(result.roots[0].name).toBe('@std/path');
457
457
+
expect(result.roots[0]!.name).toBe('@std/path');
458
458
// dependency stored under npm-compatible name, resolved to canonical
459
459
-
expect(result.roots[0].dependencies.has('@jsr/std__internal')).toBe(true);
460
460
-
const internal = result.roots[0].dependencies.get('@jsr/std__internal')!;
459
459
+
expect(result.roots[0]!.dependencies.has('@jsr/std__internal')).toBe(true);
460
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
470
-
const mainPkg = result.roots[0];
470
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
479
-
const mainPkg = result.roots[0];
479
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
487
-
expect(result.roots[0].name).toBe('use-sync-external-store');
487
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
88
-
return versions[distTags.latest] ?? null;
88
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
93
-
const taggedVersion = distTags[range];
93
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
134
-
const nonDeprecated = validVersions.find((v) => !versions[v].deprecated);
134
134
+
const nonDeprecated = validVersions.find((v) => !versions[v]!.deprecated);
135
135
if (nonDeprecated !== undefined) {
136
136
-
return versions[nonDeprecated];
136
136
+
return versions[nonDeprecated]!;
137
137
}
138
138
139
139
// fall back to deprecated if no alternatives
140
140
-
return versions[validVersions[0]];
140
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
118
-
const [prefix, suffix] = targetParts;
118
118
+
const prefix = targetParts[0]!;
119
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
124
-
const [subpathPrefix, subpathSuffix] = subpathParts;
125
125
+
const subpathPrefix = subpathParts[0]!;
126
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
273
-
defaultSubpath = entries[0].subpath;
275
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
61
-
const mainPackage = resolution.roots[0];
61
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
74
-
const packages = buildInstalledPackages(mainPackage, peerDepNames);
74
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
85
-
const id = enabled[0].id;
85
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
95
-
const id = enabled[enabled.length - 1].id;
95
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
109
-
const id = enabled[nextIndex].id;
109
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
123
-
const id = enabled[prevIndex].id;
123
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
149
-
const item = enabled[index];
149
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
70
-
items[index].el.focus();
70
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
77
-
if (!items[i].disabled) {
77
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
89
-
setFocusedIndex(enabled[0]);
89
89
+
setFocusedIndex(enabled[0]!);
90
90
};
91
91
92
92
const last = () => {
···
94
94
if (enabled.length === 0) {
95
95
return;
96
96
}
97
97
-
setFocusedIndex(enabled[enabled.length - 1]);
97
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
111
-
setFocusedIndex(enabled[nextPos]);
111
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
125
-
setFocusedIndex(enabled[prevPos]);
125
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
150
-
const index = enabled[pos];
151
151
-
const item = items[index];
150
150
+
const index = enabled[pos]!;
151
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
26
-
"noUncheckedSideEffectImports": true
26
26
+
"noUncheckedSideEffectImports": true,
27
27
+
"noUncheckedIndexedAccess": true
27
28
},
28
29
"include": ["src"]
29
30
}