Skip to content

Commit 27e3aea

Browse files
authored
add package size and dependency count to metadata (#1877)
1 parent 96f2f8e commit 27e3aea

File tree

17 files changed

+13571
-8898
lines changed

17 files changed

+13571
-8898
lines changed

assets/data.json

Lines changed: 13303 additions & 8846 deletions
Large diffs are not rendered by default.

common/styleguide.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,17 +138,19 @@ export const A = ({
138138
const [isHovered, setIsHovered] = useState(false);
139139

140140
const linkStyles = getLinkStyles(isDark);
141-
const linkHoverStyles = getLinkHoverStyles(isDark);
141+
const linkHoverStyles = getLinkHoverStyles();
142142

143143
if ((target === '_self' && !href.startsWith('#')) || href.startsWith('/')) {
144144
const passedStyle = Array.isArray(style) ? StyleSheet.flatten(style) : style;
145145
return (
146146
<Link
147147
href={href}
148+
onPointerEnter={() => setIsHovered(true)}
149+
onPointerLeave={() => setIsHovered(false)}
148150
style={{
149151
...linkStyles,
150-
...(isHovered && linkHoverStyles),
151152
...(passedStyle as any),
153+
...(isHovered && linkHoverStyles),
152154
...(isHovered && hoverStyle),
153155
}}>
154156
{children}
@@ -176,16 +178,13 @@ export const A = ({
176178

177179
const getLinkStyles = (isDark: boolean) => ({
178180
color: isDark ? colors.white : colors.black,
179-
backgroundColor: isDark ? darkColors.powder : colors.powder,
180181
textDecorationColor: isDark ? colors.gray5 : colors.pewter,
181182
textDecorationLine: 'underline',
182183
fontFamily: 'inherit',
183184
});
184185

185-
const getLinkHoverStyles = (isDark: boolean) => ({
186-
backgroundColor: isDark ? colors.primaryDark : colors.sky,
187-
color: isDark ? darkColors.dark : colors.black,
188-
textDecorationColor: isDark ? darkColors.powder : colors.gray4,
186+
const getLinkHoverStyles = () => ({
187+
textDecorationColor: colors.primaryDark,
189188
});
190189

191190
export function HoverEffect({ children }: PropsWithChildren) {

components/Icons/index.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,3 +455,26 @@ export function NativeCode({ width = 18, height = 22, fill = colors.black }: Ico
455455
</Svg>
456456
);
457457
}
458+
459+
export function PackageSize({ width = 18, height = 18, fill = colors.black }: IconProps) {
460+
return (
461+
<Svg width={width} height={height} viewBox="0 0 24 24" fill="none">
462+
<Path fill={fill} d="M1 3h13v2H3v10h18v-4h2v8H1zm3 18h16v2H4z" />
463+
<Path
464+
fill={fill}
465+
d="M20 1.002v4.586l1.501-1.5L22.915 5.5l-3.914 3.915L15.084 5.5l1.414-1.414l1.503 1.503V1.002z"
466+
/>
467+
</Svg>
468+
);
469+
}
470+
471+
export function Dependency({ width = 18, height = 20, fill = colors.black }: IconProps) {
472+
return (
473+
<Svg width={width} height={height} viewBox="0 0 18 20" fill="none">
474+
<Path
475+
fill={fill}
476+
d="M8 17.425V10.575L2 7.1V13.95L8 17.425ZM10 17.425L16 13.95V7.1L10 10.575V17.425ZM8 19.725L1 15.7C0.683337 15.5167 0.437337 15.275 0.262004 14.975C0.0866705 14.675 -0.000662879 14.3417 3.78787e-06 13.975V6.025C3.78787e-06 5.65833 0.0876704 5.325 0.263004 5.025C0.438337 4.725 0.684004 4.48333 1 4.3L8 0.275C8.31667 0.0916668 8.65 0 9 0C9.35 0 9.68333 0.0916668 10 0.275L17 4.3C17.3167 4.48333 17.5627 4.725 17.738 5.025C17.9133 5.325 18.0007 5.65833 18 6.025V13.975C18 14.3417 17.9123 14.675 17.737 14.975C17.5617 15.275 17.316 15.5167 17 15.7L10 19.725C9.68333 19.9083 9.35 20 9 20C8.65 20 8.31667 19.9083 8 19.725ZM13 6.525L14.925 5.425L9 2L7.05 3.125L13 6.525ZM9 8.85L10.95 7.725L5.025 4.3L3.075 5.425L9 8.85Z"
477+
/>
478+
</Svg>
479+
);
480+
}

components/Library/MetaData.tsx

Lines changed: 94 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ import { useContext } from 'react';
22
import { Platform, StyleSheet, View } from 'react-native';
33

44
import { colors, A, P, Caption, darkColors } from '~/common/styleguide';
5+
import Tooltip from '~/components/Tooltip';
56
import CustomAppearanceContext from '~/context/CustomAppearanceContext';
6-
import { type LibraryType } from '~/types';
7+
import { type LibraryType, MetadataEntryType } from '~/types';
8+
import { partition } from '~/util/arrays';
9+
import { formatBytes } from '~/util/formatBytes';
710

811
import { DirectoryScore } from './DirectoryScore';
912
import {
@@ -17,25 +20,26 @@ import {
1720
Code,
1821
TypeScript,
1922
NativeCode,
23+
PackageSize,
24+
Dependency,
2025
} from '../Icons';
2126

2227
type Props = {
2328
library: LibraryType;
2429
secondary?: boolean;
2530
};
2631

27-
function generateData({ github, score, npm, npmPkg }: LibraryType, isDark: boolean) {
32+
function generateData(
33+
{ github, score, npm, npmPkg }: LibraryType,
34+
isDark: boolean
35+
): MetadataEntryType[] {
2836
const iconColor = isDark ? darkColors.pewter : colors.gray5;
2937
return [
3038
{
3139
id: 'score',
3240
icon: <DirectoryScore score={score} />,
3341
content: (
34-
<A
35-
target="_self"
36-
href="/scoring"
37-
style={{ ...styles.link, ...styles.mutedLink }}
38-
hoverStyle={isDark && { color: colors.primaryDark }}>
42+
<A target="_self" href="/scoring" style={styles.link}>
3943
Directory Score
4044
</A>
4145
),
@@ -63,15 +67,38 @@ function generateData({ github, score, npm, npmPkg }: LibraryType, isDark: boole
6367
</A>
6468
),
6569
},
70+
{
71+
id: 'dependencies',
72+
icon: <Dependency fill={iconColor} />,
73+
content: (
74+
<A
75+
href={`https://www.npmjs.com/package/${npmPkg}?activeTab=dependencies`}
76+
style={styles.link}>
77+
{`${github.stats.dependencies} ${github.stats.dependencies === 1 ? 'dependency' : 'dependencies'}`}
78+
</A>
79+
),
80+
},
81+
npm.size
82+
? {
83+
id: 'size',
84+
icon: <PackageSize fill={iconColor} />,
85+
content: (
86+
<A href={`https://packagephobia.com/result?p=${npmPkg}`} style={styles.link}>
87+
{`${formatBytes(npm.size)} package size`}
88+
</A>
89+
),
90+
}
91+
: null,
6692
github.stats.forks
6793
? {
6894
id: 'forks',
6995
icon: <Fork fill={iconColor} width={16} height={17} />,
7096
content: (
7197
<A href={`${github.urls.repo}/network/members`} style={styles.link}>
72-
{`${github.stats.forks.toLocaleString()}`} forks
98+
{`${github.stats.forks.toLocaleString()}`}
7399
</A>
74100
),
101+
tooltip: 'Forks',
75102
}
76103
: null,
77104
github.stats.subscribers
@@ -80,9 +107,10 @@ function generateData({ github, score, npm, npmPkg }: LibraryType, isDark: boole
80107
icon: <Eye fill={iconColor} />,
81108
content: (
82109
<A href={`${github.urls.repo}/watchers`} style={styles.link}>
83-
{`${github.stats.subscribers.toLocaleString()}`} watchers
110+
{`${github.stats.subscribers.toLocaleString()}`}
84111
</A>
85112
),
113+
tooltip: 'Watchers',
86114
}
87115
: null,
88116
github.stats.issues
@@ -91,9 +119,10 @@ function generateData({ github, score, npm, npmPkg }: LibraryType, isDark: boole
91119
icon: <Issue fill={iconColor} />,
92120
content: (
93121
<A href={`${github.urls.repo}/issues`} style={styles.link}>
94-
{`${github.stats.issues.toLocaleString()}`} issues
122+
{`${github.stats.issues.toLocaleString()}`}
95123
</A>
96124
),
125+
tooltip: 'Issues',
97126
}
98127
: null,
99128
];
@@ -106,7 +135,10 @@ function generateSecondaryData({ github, examples }: LibraryType, isDark: boolea
106135
const iconColor = isDark ? darkColors.pewter : colors.secondary;
107136
const paragraphStyles = [styles.secondaryText, secondaryTextColor];
108137
const linkStyles = [...paragraphStyles, styles.mutedLink];
109-
const hoverStyle = isDark && { color: colors.primaryDark };
138+
const hoverStyle = [
139+
{ textDecorationColor: isDark ? colors.gray6 : colors.gray4 },
140+
isDark && { color: colors.primaryDark },
141+
];
110142

111143
return [
112144
github.urls.homepage
@@ -173,26 +205,57 @@ function generateSecondaryData({ github, examples }: LibraryType, isDark: boolea
173205

174206
export function MetaData({ library, secondary }: Props) {
175207
const { isDark } = useContext(CustomAppearanceContext);
176-
const data = secondary ? generateSecondaryData(library, isDark) : generateData(library, isDark);
177208

178-
return (
179-
<>
180-
{data.filter(Boolean).map(({ id, icon, content }, i) => (
181-
<View
182-
key={id}
183-
style={[
184-
styles.displayHorizontal,
185-
i + 1 !== data.length ? styles.datumContainer : {},
186-
secondary ? styles.secondaryContainer : {},
187-
]}>
188-
<View style={[styles.iconContainer, secondary ? styles.secondaryIconContainer : {}]}>
189-
{icon}
209+
if (secondary) {
210+
const data = generateSecondaryData(library, isDark).filter(Boolean);
211+
return (
212+
<>
213+
{data.map(({ id, icon, content }, i) => (
214+
<View
215+
key={id}
216+
style={[
217+
styles.displayHorizontal,
218+
i + 1 !== data.length ? styles.datumContainer : {},
219+
styles.secondaryContainer,
220+
]}>
221+
<View style={[styles.iconContainer, styles.secondaryIconContainer]}>{icon}</View>
222+
{content}
190223
</View>
191-
{content}
224+
))}
225+
</>
226+
);
227+
} else {
228+
const data = generateData(library, isDark).filter(entry => !!entry);
229+
const [bottomData, listData] = partition<NonNullable<MetadataEntryType>>(data, ({ id }) => {
230+
return ['forks', 'subscribers', 'issues'].includes(id);
231+
});
232+
return (
233+
<>
234+
{listData.map(({ id, icon, content }, i) => (
235+
<View
236+
key={id}
237+
style={[styles.displayHorizontal, i + 1 !== data.length ? styles.datumContainer : {}]}>
238+
<View style={styles.iconContainer}>{icon}</View>
239+
{content}
240+
</View>
241+
))}
242+
<View style={[styles.displayHorizontal, styles.bottomStats]}>
243+
{bottomData.map(({ id, icon, content, tooltip }) => (
244+
<View key={id} style={styles.displayHorizontal}>
245+
<Tooltip
246+
key={id}
247+
sideOffset={2}
248+
delayDuration={100}
249+
trigger={<View style={styles.iconContainer}>{icon}</View>}>
250+
{tooltip}
251+
</Tooltip>
252+
{content}
253+
</View>
254+
))}
192255
</View>
193-
))}
194-
</>
195-
);
256+
</>
257+
);
258+
}
196259
}
197260

198261
const styles = StyleSheet.create({
@@ -205,6 +268,9 @@ const styles = StyleSheet.create({
205268
flexDirection: 'row',
206269
alignItems: 'center',
207270
},
271+
bottomStats: {
272+
gap: 16,
273+
},
208274
iconContainer: {
209275
marginRight: 7,
210276
width: 22,

components/Library/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ const Library = ({ library, skipMetadata, showTrendingMark }: Props) => {
9292
<View style={styles.verticalMargin}>
9393
<CompatibilityTags library={library} />
9494
</View>
95-
{github.description && github.description.length && (
95+
{Boolean(github.description && github.description.length) && (
9696
<View style={styles.verticalMargin}>
9797
<Headline
9898
numberOfLines={skipMetadata ? 3 : undefined}

components/Pagination.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { Arrow } from './Icons';
1313

1414
type Props = {
1515
query: Query;
16-
total: number | null;
16+
total?: number | null;
1717
style?: ViewStyle;
1818
basePath?: string;
1919
};

components/Search.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ const Search = ({ query, total }: Props) => {
120120
Esc
121121
</Label>
122122
<Label style={styles.focusHintLabel}>
123-
to {search?.length > 0 ? 'clear' : 'blur'}
123+
to {(search?.length ?? 0) > 0 ? 'clear' : 'blur'}
124124
</Label>
125125
</View>
126126
) : (

components/Tooltip.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import { type PopperContentProps } from '@radix-ui/react-popper';
21
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
32
import type { PropsWithChildren, ReactNode } from 'react';
43

54
type TooltipProps = PropsWithChildren<{
65
trigger: ReactNode;
7-
sideOffset?: number;
8-
side?: PopperContentProps['side'];
6+
sideOffset?: TooltipPrimitive.TooltipContentProps['sideOffset'];
7+
side?: TooltipPrimitive.TooltipContentProps['side'];
8+
delayDuration?: TooltipPrimitive.TooltipProps['delayDuration'];
99
}>;
1010

11-
function Tooltip({ children, trigger, side, sideOffset = 4 }: TooltipProps) {
11+
function Tooltip({ children, trigger, side, delayDuration = 0, sideOffset = 4 }: TooltipProps) {
1212
return (
1313
<TooltipPrimitive.Provider>
14-
<TooltipPrimitive.Root delayDuration={0}>
14+
<TooltipPrimitive.Root delayDuration={delayDuration}>
1515
<TooltipPrimitive.Trigger asChild>{trigger}</TooltipPrimitive.Trigger>
1616
<TooltipPrimitive.Portal>
1717
<TooltipPrimitive.Content className="TooltipContent" sideOffset={sideOffset} side={side}>

scripts/build-and-score-data.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import path from 'node:path';
66

77
import debugGithubRepos from '~/debug-github-repos.json';
88
import githubRepos from '~/react-native-libraries.json';
9+
import { fetchNpmRegistryData } from '~/scripts/fetch-npm-registry-data';
910
import { fetchNpmStatDataBulk } from '~/scripts/fetch-npm-stat-data';
1011
import { LibraryDataEntryType, LibraryType } from '~/types';
1112
import { isLaterThan, TimeRange } from '~/util/datetime';
@@ -115,6 +116,10 @@ async function buildAndScoreData() {
115116
},
116117
}));
117118

119+
console.log('\n⬇️ Fetching registry data from npm');
120+
121+
data = await fetchNpmRegistryDataSequentially(data);
122+
118123
console.log('\n⚛️ Calculating Directory Score');
119124
data = data.map(project => {
120125
try {
@@ -370,4 +375,27 @@ async function fetchNpmStatDataSequentially(bulkList: string[][]) {
370375
return results;
371376
}
372377

378+
async function fetchNpmRegistryDataSequentially(list: LibraryType[]) {
379+
const total = list.length;
380+
381+
for (let i = 0; i < list.length; i++) {
382+
const entry = list[i];
383+
384+
if (entry.template) {
385+
continue;
386+
}
387+
388+
await sleep(SLEEP_TIME / 10);
389+
const shouldLog = i % CHUNK_SIZE === 0;
390+
shouldLog && console.log(`Sleeping ${SLEEP_TIME / 10}ms`);
391+
392+
const data = await fetchNpmRegistryData(entry);
393+
shouldLog && console.log(`${CHUNK_SIZE * Math.floor(i / CHUNK_SIZE)} of ${total} fetched`);
394+
395+
list[i] = data;
396+
}
397+
398+
return list;
399+
}
400+
373401
await buildAndScoreData();

scripts/fetch-github-data.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ const createRepoDataWithResponse = (json, monorepo) => {
133133
json.name = packageJson.name;
134134
json.isPackagePrivate = packageJson.private ?? false;
135135
json.registry = packageJson?.publishConfig?.registry ?? undefined;
136+
json.dependenciesCount = packageJson.dependencies
137+
? Object.keys(packageJson.dependencies).length
138+
: 0;
136139

137140
if (monorepo) {
138141
json.homepageUrl = packageJson.homepage;
@@ -192,6 +195,7 @@ const createRepoDataWithResponse = (json, monorepo) => {
192195
issues: json.issues.totalCount,
193196
subscribers: json.watchers.totalCount,
194197
stars: json.stargazers.totalCount,
198+
dependencies: json.dependenciesCount,
195199
},
196200
name: json.name,
197201
fullName: json.nameWithOwner,

0 commit comments

Comments
 (0)