diff --git a/app/components/LicenseDisplay.vue b/app/components/LicenseDisplay.vue index c83cb46d35..95f87dba5a 100644 --- a/app/components/LicenseDisplay.vue +++ b/app/components/LicenseDisplay.vue @@ -1,11 +1,32 @@ @@ -21,7 +42,7 @@ const hasAnyValidLicense = computed(() => tokens.value.some(t => t.type === 'lic class="link-subtle" :title="$t('package.license.view_spdx')" > - {{ token.value }} + {{ token.value }}!! {{ token.value }} {{ token.value }} @@ -32,4 +53,26 @@ const hasAnyValidLicense = computed(() => tokens.value.some(t => t.type === 'lic aria-hidden="true" /> +
+

change!

+ + + + + + +
diff --git a/app/composables/useLicenseChanges.ts b/app/composables/useLicenseChanges.ts new file mode 100644 index 0000000000..74b0f0deab --- /dev/null +++ b/app/composables/useLicenseChanges.ts @@ -0,0 +1,80 @@ +import type { MaybeRefOrGetter } from 'vue' +import { toValue } from 'vue' + +export interface LicenseChange { + from: string + to: string + version: string +} + +export interface LicenseChangesResult { + changes: LicenseChange[] +} + +// Type definitions for npm registry response +interface NpmRegistryVersion { + version: string + license?: string +} + +// for registry responses of $fetch function, the type includes the key versions as well as many others too. +interface NpmRegistryResponse { + versions: Record< + string, + { + version: string + license?: string + } + > +} + +/** + * Composable to detect license changes across all versions of a package + */ +export function useLicenseChanges(packageName: MaybeRefOrGetter) { + return useAsyncData( + () => `license-changes:${toValue(packageName)}`, + async () => { + const name = toValue(packageName) + if (!name) return { changes: [] } + + // Fetch full package metadata from npm registry + const url = `https://registry.npmjs.org/${name}` + const data = await $fetch(url) + + const changes: LicenseChange[] = [] + let prevLicense: string | undefined = undefined + + // `data.versions` is an object with version keys + const versions = Object.values(data.versions) as NpmRegistryVersion[] + + // Sort versions ascending to compare chronologically + versions.sort((a, b) => { + const parse = (v: string) => v.split('.').map(Number) + const [aMajor, aMinor, aPatch] = parse(a.version as string) + const [bMajor, bMinor, bPatch] = parse(b.version as string) + if (aMajor !== bMajor) return aMajor! - bMajor! + if (aMinor !== bMinor) return aMinor! - bMinor! + return aPatch! - bPatch! + }) + + // Detect license changes + for (const version of versions) { + const license = (version.license as string) ?? 'UNKNOWN' + if (prevLicense && license !== prevLicense) { + changes.push({ + from: prevLicense, + to: license, + version: version.version as string, + }) + } + prevLicense = license + } + return { changes } + }, + { + default: () => ({ changes: [] }), + watch: [() => toValue(packageName)], + }, + ) +} diff --git a/app/pages/package/[[org]]/[name].vue b/app/pages/package/[[org]]/[name].vue index a83a101243..75a5c5f16d 100644 --- a/app/pages/package/[[org]]/[name].vue +++ b/app/pages/package/[[org]]/[name].vue @@ -583,7 +583,11 @@ const showSkeleton = shallowRef(false) {{ $t('package.stats.license') }}
- + {{ $t('package.license.none') }}
diff --git a/i18n/locales/en.json b/i18n/locales/en.json index f54f447012..3605a48203 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -431,7 +431,10 @@ "filter_placeholder": "Filter by semver (e.g. ^3.0.0)", "filter_invalid": "Invalid semver range", "filter_help": "Semver range filter help", + "license_change_help": "License Change Details", + "license_change_item": "from {from} to {to} at version {version}", "filter_tooltip": "Filter versions using a {link}. For example, ^3.0.0 shows all 3.x versions.", + "changed_license": "The license was changed: {license_change}", "filter_tooltip_link": "semver range", "no_matches": "No versions match this range", "copy_alt": { diff --git a/i18n/locales/tr-TR.json b/i18n/locales/tr-TR.json index c1b0a40b50..2920f2d436 100644 --- a/i18n/locales/tr-TR.json +++ b/i18n/locales/tr-TR.json @@ -386,7 +386,10 @@ "filter_placeholder": "Semver ile filtrele (örn. ^3.0.0)", "filter_invalid": "Geçersiz semver aralığı", "filter_help": "Semver aralığı filtresi yardımı", + "license_change_help": "Lisans değişikliği yardımı", + "license_change_item": "{version} sürümünde {from}'den {to}'ya", "filter_tooltip": "Sürümleri {link} kullanarak filtreleyin. Örneğin, ^3.0.0 tüm 3.x sürümlerini gösterir.", + "changed_license": "Lisans değişikliği gerçekleşti: {license_change}", "filter_tooltip_link": "semver aralığı", "no_matches": "Bu aralığa uygun sürüm yok", "copy_alt": { diff --git a/i18n/schema.json b/i18n/schema.json index 151a19c977..07d7b04b3e 100644 --- a/i18n/schema.json +++ b/i18n/schema.json @@ -1297,9 +1297,18 @@ "filter_help": { "type": "string" }, + "license_change_help": { + "type": "string" + }, + "license_change_item": { + "type": "string" + }, "filter_tooltip": { "type": "string" }, + "changed_license": { + "type": "string" + }, "filter_tooltip_link": { "type": "string" },