Skip to content

Commit e035b02

Browse files
authored
Merge pull request #1 from bamlab/claude/slack-remove-the-limit-pt7mU
Remove npm package limit by fetching @bam.tech packages dynamically
2 parents a1c56eb + 9e5f92c commit e035b02

4 files changed

Lines changed: 54 additions & 34 deletions

File tree

‎scripts/fetch-data.ts‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import { writeFileSync, mkdirSync } from 'fs';
1313
import { join, dirname } from 'path';
1414
import { fileURLToPath } from 'url';
1515

16-
import { PACKAGES } from '../src/constants/packages';
17-
import { fetchDownloads, fetchRegistry, NotFoundError } from '../src/lib/npmApi';
16+
import { UNSCOPED_PACKAGES } from '../src/constants/packages';
17+
import { fetchDownloads, fetchRegistry, fetchScopedPackages, NotFoundError } from '../src/lib/npmApi';
1818
import { fetchStars } from '../src/lib/githubApi';
1919
import {
2020
bucketByMonth,
@@ -171,8 +171,21 @@ async function main() {
171171
const __dirname = dirname(fileURLToPath(import.meta.url));
172172
const outPath = join(__dirname, '../public/data.json');
173173

174+
// Fetch @bam.tech scoped packages dynamically
175+
console.log('Fetching @bam.tech scoped npm packages...');
176+
let scopedPackages: string[] = [];
177+
try {
178+
scopedPackages = await fetchScopedPackages('bam.tech');
179+
console.log(` Found ${scopedPackages.length} scoped npm packages`);
180+
} catch (err) {
181+
console.error(` npm scope fetch failed: ${(err as Error).message}`);
182+
}
183+
184+
// Combine scoped packages with unscoped packages
185+
const npmPackages = [...scopedPackages, ...UNSCOPED_PACKAGES];
186+
174187
// Fetch pub.dev package list for bam.tech publisher
175-
console.log('Fetching pub.dev packages for bam.tech...');
188+
console.log('\nFetching pub.dev packages for bam.tech...');
176189
let pubPackages: string[] = [];
177190
try {
178191
pubPackages = await fetchPubPublisherPackages('bam.tech');
@@ -181,9 +194,9 @@ async function main() {
181194
console.error(` pub.dev publisher fetch failed: ${(err as Error).message}`);
182195
}
183196

184-
console.log(`\nFetching data for ${PACKAGES.length} npm packages...`);
197+
console.log(`\nFetching data for ${npmPackages.length} npm packages...`);
185198
const npmResults = await Promise.all(
186-
PACKAGES.map(async (name) => {
199+
npmPackages.map(async (name) => {
187200
try {
188201
const result = await processNpmPackage(name);
189202
const status = result.notFound

‎src/constants/packages.ts‎

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
1-
export const PACKAGES = [
2-
// Scoped @bam.tech packages
3-
'@bam.tech/react-native-image-resizer',
4-
'@bam.tech/react-navigation-visualizer-dev-plugin',
5-
'@bam.tech/react-native-app-security',
6-
'@bam.tech/react-native-make',
7-
'@bam.tech/eslint-plugin',
8-
'@bam.tech/react-native-config',
9-
'@bam.tech/react-native-keyevent-expo-config-plugin',
10-
'@bam.tech/lrud',
11-
'@bam.tech/flow-navigator',
12-
'@bam.tech/react-native-graphql-transformer',
13-
'@bam.tech/tanstack-query-detect-waterfall',
14-
'@bam.tech/react-native-batch',
15-
// Unscoped packages
1+
/**
2+
* Unscoped npm packages maintained by BAM.
3+
* These cannot be discovered via the npm scope search API, so they are hardcoded.
4+
* Scoped @bam.tech/* packages are fetched dynamically.
5+
*/
6+
export const UNSCOPED_PACKAGES = [
167
'react-native-performance',
178
'react-native-image-resizer',
189
'react-native-hide-with-keyboard',
@@ -36,4 +27,4 @@ export const PACKAGES = [
3627
'react-native-text-input',
3728
] as const;
3829

39-
export type PackageName = (typeof PACKAGES)[number];
30+
export type UnscopedPackageName = (typeof UNSCOPED_PACKAGES)[number];

‎src/hooks/useAllPackageData.ts‎

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
11
import { useQuery } from '@tanstack/react-query';
2-
import { PACKAGES } from '../constants/packages';
32
import type { DataFile, PackageData } from '../types';
43

5-
const EMPTY_PACKAGES: PackageData[] = PACKAGES.map((name) => ({
6-
name,
7-
totalDownloads: 0,
8-
monthlyDownloads: [],
9-
momGrowthPct: null,
10-
isGrowing: false,
11-
stars: null,
12-
description: null,
13-
notFound: false,
14-
}));
15-
164
export function useAllPackageData(): { packages: PackageData[]; isLoading: boolean; generatedAt: string | null } {
175
const { data, isLoading } = useQuery<DataFile>({
186
queryKey: ['data.json'],
@@ -22,7 +10,7 @@ export function useAllPackageData(): { packages: PackageData[]; isLoading: boole
2210
});
2311

2412
return {
25-
packages: data?.packages ?? EMPTY_PACKAGES,
13+
packages: data?.packages ?? [],
2614
isLoading,
2715
generatedAt: data?.generatedAt ?? null,
2816
};

‎src/lib/npmApi.ts‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@ import type { NpmDownloadResponse, NpmRegistryResponse } from '../types';
22

33
const DOWNLOADS_BASE = 'https://api.npmjs.org/downloads/range';
44
const REGISTRY_BASE = 'https://registry.npmjs.org';
5+
const SEARCH_BASE = 'https://registry.npmjs.org/-/v1/search';
6+
7+
interface NpmSearchResponse {
8+
objects: Array<{ package: { name: string } }>;
9+
total: number;
10+
}
11+
12+
/**
13+
* Fetches all npm packages from a given scope (e.g., 'bam.tech' for @bam.tech/*)
14+
*/
15+
export async function fetchScopedPackages(scope: string): Promise<string[]> {
16+
const allNames: string[] = [];
17+
let from = 0;
18+
const size = 250; // npm search API max per page
19+
20+
while (true) {
21+
const url = `${SEARCH_BASE}?text=scope:${encodeURIComponent(scope)}&size=${size}&from=${from}`;
22+
const res = await fetch(url);
23+
if (!res.ok) throw new Error(`npm search API error ${res.status} for scope ${scope}`);
24+
const data: NpmSearchResponse = await res.json();
25+
if (data.objects.length === 0) break;
26+
allNames.push(...data.objects.map((o) => o.package.name));
27+
from += data.objects.length;
28+
if (from >= data.total) break;
29+
}
30+
31+
return allNames;
32+
}
533

634
// Fetch last 18 months of daily download data
735
export async function fetchDownloads(packageName: string): Promise<NpmDownloadResponse> {

0 commit comments

Comments
 (0)