|
1 | 1 | import type { |
2 | 2 | Packument, |
| 3 | + PackumentVersion, |
| 4 | + SlimPackument, |
3 | 5 | NpmSearchResponse, |
4 | 6 | NpmDownloadCount, |
5 | 7 | NpmDownloadRange, |
@@ -66,12 +68,78 @@ function encodePackageName(name: string): string { |
66 | 68 | return encodeURIComponent(name) |
67 | 69 | } |
68 | 70 |
|
| 71 | +/** Maximum number of versions to include in the client payload */ |
| 72 | +const MAX_VERSIONS = 20 |
| 73 | + |
| 74 | +/** |
| 75 | + * Transform a full Packument into a slimmed version for client-side use. |
| 76 | + * Reduces payload size by: |
| 77 | + * - Removing readme (fetched separately) |
| 78 | + * - Limiting versions to recent MAX_VERSIONS |
| 79 | + * - Stripping unnecessary fields from version objects |
| 80 | + */ |
| 81 | +function transformPackument(pkg: Packument): SlimPackument { |
| 82 | + // Sort versions by publish time (newest first) |
| 83 | + const sortedVersionKeys = Object.keys(pkg.versions) |
| 84 | + .filter(v => pkg.time[v]) // Only versions with timestamps |
| 85 | + .sort((a, b) => { |
| 86 | + const timeA = pkg.time[a] |
| 87 | + const timeB = pkg.time[b] |
| 88 | + if (!timeA || !timeB) return 0 |
| 89 | + return new Date(timeB).getTime() - new Date(timeA).getTime() |
| 90 | + }) |
| 91 | + .slice(0, MAX_VERSIONS) |
| 92 | + |
| 93 | + // Always include the latest dist-tag version even if not in top MAX_VERSIONS |
| 94 | + const latestTag = pkg['dist-tags']?.latest |
| 95 | + if (latestTag && !sortedVersionKeys.includes(latestTag)) { |
| 96 | + sortedVersionKeys.push(latestTag) |
| 97 | + } |
| 98 | + |
| 99 | + // Build filtered versions object |
| 100 | + const filteredVersions: Record<string, PackumentVersion> = {} |
| 101 | + for (const v of sortedVersionKeys) { |
| 102 | + const version = pkg.versions[v] |
| 103 | + if (version) { |
| 104 | + // Strip readme and scripts from each version to reduce size |
| 105 | + const { readme: _readme, scripts: _scripts, ...slimVersion } = version |
| 106 | + filteredVersions[v] = slimVersion as PackumentVersion |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + // Build filtered time object (only for included versions) |
| 111 | + const filteredTime: Record<string, string> = {} |
| 112 | + if (pkg.time.modified) filteredTime.modified = pkg.time.modified |
| 113 | + if (pkg.time.created) filteredTime.created = pkg.time.created |
| 114 | + for (const v of sortedVersionKeys) { |
| 115 | + if (pkg.time[v]) filteredTime[v] = pkg.time[v] |
| 116 | + } |
| 117 | + |
| 118 | + return { |
| 119 | + '_id': pkg._id, |
| 120 | + '_rev': pkg._rev, |
| 121 | + 'name': pkg.name, |
| 122 | + 'description': pkg.description, |
| 123 | + 'dist-tags': pkg['dist-tags'], |
| 124 | + 'time': filteredTime, |
| 125 | + 'maintainers': pkg.maintainers, |
| 126 | + 'author': pkg.author, |
| 127 | + 'license': pkg.license, |
| 128 | + 'homepage': pkg.homepage, |
| 129 | + 'keywords': pkg.keywords, |
| 130 | + 'repository': pkg.repository, |
| 131 | + 'bugs': pkg.bugs, |
| 132 | + 'versions': filteredVersions, |
| 133 | + } |
| 134 | +} |
| 135 | + |
69 | 136 | export function usePackage(name: MaybeRefOrGetter<string>) { |
70 | 137 | const registry = useNpmRegistry() |
71 | 138 |
|
72 | 139 | return useLazyAsyncData( |
73 | 140 | () => `package:${toValue(name)}`, |
74 | 141 | () => registry.fetchPackage(toValue(name)), |
| 142 | + { transform: transformPackument }, |
75 | 143 | ) |
76 | 144 | } |
77 | 145 |
|
|
0 commit comments