From e7e612dedc93f0a4f4a238ebdc694b4f251e71c8 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Thu, 26 Feb 2026 15:37:43 +0000 Subject: [PATCH 1/6] feat: show a warning when significant size increase When viewing a package, this shows a warning box making the user aware that the number of dependencies or the install size significantly increased. It sets the thresholds as: - Significant size increase = 25% - Significant dependency count increase = >5 --- app/components/Package/SizeIncrease.vue | 45 ++++ app/composables/useInstallSizeDiff.ts | 111 +++++++++ app/pages/package/[[org]]/[name].vue | 13 +- i18n/locales/en.json | 7 + i18n/schema.json | 21 ++ lunaria/files/en-GB.json | 7 + lunaria/files/en-US.json | 7 + server/utils/install-size.ts | 26 --- shared/types/index.ts | 1 + shared/types/install-size.ts | 22 ++ .../composables/use-install-size-diff.spec.ts | 216 ++++++++++++++++++ 11 files changed, 443 insertions(+), 33 deletions(-) create mode 100644 app/components/Package/SizeIncrease.vue create mode 100644 app/composables/useInstallSizeDiff.ts create mode 100644 shared/types/install-size.ts create mode 100644 test/nuxt/composables/use-install-size-diff.spec.ts diff --git a/app/components/Package/SizeIncrease.vue b/app/components/Package/SizeIncrease.vue new file mode 100644 index 0000000000..3f8dbeecd1 --- /dev/null +++ b/app/components/Package/SizeIncrease.vue @@ -0,0 +1,45 @@ + + + diff --git a/app/composables/useInstallSizeDiff.ts b/app/composables/useInstallSizeDiff.ts new file mode 100644 index 0000000000..76c300f942 --- /dev/null +++ b/app/composables/useInstallSizeDiff.ts @@ -0,0 +1,111 @@ +import { compare, prerelease, valid } from 'semver' +import type { InstallSizeResult, SlimPackument } from '#shared/types' + +export interface InstallSizeDiff { + comparisonVersion: string + sizeRatio: number + sizeIncrease: number + currentSize: number + previousSize: number + depDiff: number + currentDeps: number + previousDeps: number + sizeThresholdExceeded: boolean + depThresholdExceeded: boolean +} + +const SIZE_INCREASE_THRESHOLD = 0.25 +const DEP_INCREASE_THRESHOLD = 5 + +function getComparisonVersion(pkg: SlimPackument, resolvedVersion: string): string | null { + const isCurrentPrerelease = prerelease(resolvedVersion) !== null + + if (isCurrentPrerelease) { + const latest = pkg['dist-tags']?.latest + if (!latest || latest === resolvedVersion) return null + return latest + } + + // Find the previous version in time that was stable + const stableVersions = Object.keys(pkg.time) + .filter(v => v !== 'modified' && v !== 'created' && valid(v) !== null && prerelease(v) === null) + .sort((a, b) => compare(a, b)) + + const currentIdx = stableVersions.indexOf(resolvedVersion) + if (currentIdx <= 0) return null + + return stableVersions[currentIdx - 1]! +} + +export function useInstallSizeDiff( + packageName: MaybeRefOrGetter, + resolvedVersion: MaybeRefOrGetter, + pkg: MaybeRefOrGetter, + currentInstallSize: MaybeRefOrGetter, +) { + const comparisonVersion = computed(() => { + const pkgVal = toValue(pkg) + const version = toValue(resolvedVersion) + if (!pkgVal || !version) return null + return getComparisonVersion(pkgVal, version) + }) + + const { + data: comparisonInstallSize, + status: comparisonStatus, + execute: fetchComparisonSize, + } = useLazyFetch( + () => { + const v = comparisonVersion.value + if (!v) return '' + return `/api/registry/install-size/${toValue(packageName)}/v/${v}` + }, + { + server: false, + immediate: false, + default: () => null, + }, + ) + + if (import.meta.client) { + watch( + comparisonVersion, + v => { + if (v) fetchComparisonSize() + }, + { immediate: true }, + ) + } + + const diff = computed(() => { + const current = toValue(currentInstallSize) + const previous = comparisonInstallSize.value + const cv = comparisonVersion.value + + if (!current || !previous || !cv) return null + + const sizeRatio = + previous.totalSize > 0 ? (current.totalSize - previous.totalSize) / previous.totalSize : 0 + const depDiff = current.dependencyCount - previous.dependencyCount + + const sizeThresholdExceeded = sizeRatio > SIZE_INCREASE_THRESHOLD + const depThresholdExceeded = depDiff > DEP_INCREASE_THRESHOLD + + if (!sizeThresholdExceeded && !depThresholdExceeded) return null + + return { + comparisonVersion: cv, + sizeRatio, + sizeIncrease: current.totalSize - previous.totalSize, + currentSize: current.totalSize, + previousSize: previous.totalSize, + depDiff, + currentDeps: current.dependencyCount, + previousDeps: previous.dependencyCount, + sizeThresholdExceeded, + depThresholdExceeded, + } + }) + + return { diff, comparisonVersion, comparisonStatus } +} diff --git a/app/pages/package/[[org]]/[name].vue b/app/pages/package/[[org]]/[name].vue index 1d81f70eba..1553b0f049 100644 --- a/app/pages/package/[[org]]/[name].vue +++ b/app/pages/package/[[org]]/[name].vue @@ -1,5 +1,6 @@