-
-
Notifications
You must be signed in to change notification settings - Fork 425
feat: add a warning when the package license changes #2188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
3a0543f
bfd9550
fa52bff
bd4ebe5
9afe21d
ecbb660
b91aab5
34b84c5
302952a
45e2528
e4fd978
a5b4e09
61432b3
ae2ac2a
18a58a1
ef3d106
0fac1ce
c3b5986
e1a28da
083e17f
b397996
1cd6227
4b2d0b9
db9c4ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,34 @@ | ||
| <script setup lang="ts"> | ||
| import { parseLicenseExpression } from '#shared/utils/spdx' | ||
|
|
||
| import { useLicenseChanges } from '~/composables/useLicenseChanges' | ||
| import { useI18n } from 'vue-i18n' | ||
|
|
||
| const props = defineProps<{ | ||
| license: string | ||
| packageName?: string | ||
| }>() | ||
|
|
||
| const { t } = useI18n() | ||
|
|
||
| const tokens = computed(() => parseLicenseExpression(props.license)) | ||
| const licenseChanges = useLicenseChanges(() => props.packageName) | ||
|
|
||
| const changes = computed(() => licenseChanges.data.value?.changes ?? []) | ||
|
|
||
| const licenseChangeText = computed(() => | ||
| changes.value | ||
| .map(item => | ||
| t('package.versions.license_change_item', { | ||
| from: item.from, | ||
| to: item.to, | ||
| version: item.version, | ||
| }), | ||
| ) | ||
| .join('; '), | ||
| ) | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| const hasAnyValidLicense = computed(() => tokens.value.some(t => t.type === 'license' && t.url)) | ||
| </script> | ||
|
|
||
| <template> | ||
|
|
@@ -32,4 +53,31 @@ | |
| aria-hidden="true" | ||
| /> | ||
| </span> | ||
| <div | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| v-if="changes.length > 0" | ||
| class="border border-amber-600/40 bg-amber-500/10 rounded-lg inline-flex justify-start items-center mt-1 gap-x-1 py-[2px] px-[3px]" | ||
| > | ||
| <p class="text-md text-amber-800 dark:text-amber-400"> | ||
| {{ $t('package.versions.license_change_warning') }} | ||
| </p> | ||
| <TooltipApp interactive position="top"> | ||
| <span | ||
| tabindex="0" | ||
| class="block cursor-help shrink-0 -m-2 p-2 -me-1 focus-visible:outline-2 focus-visible:outline-accent/70 rounded" | ||
| > | ||
| <span | ||
| class="block i-lucide:info w-3.5 h-3.5 text-fg-subtle" | ||
| role="img" | ||
| :aria-label="$t('package.versions.license_change_help')" | ||
| /> | ||
| </span> | ||
| <template #content> | ||
| <p class="text-xs text-fg-muted"> | ||
| <i18n-t keypath="package.versions.changed_license" tag="span"> | ||
| <template #license_change>{{ licenseChangeText }}</template> | ||
| </i18n-t> | ||
| </p> | ||
| </template> | ||
| </TooltipApp> | ||
| </div> | ||
| </template> | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,74 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| 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 { | ||||||||||||||||||||||||||||||||||||||||||||
| time: Record<string, string> | ||||||||||||||||||||||||||||||||||||||||||||
| versions: Record<string, NpmRegistryVersion> | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||
| * Composable to detect license changes across all versions of a package | ||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||
| export function useLicenseChanges(packageName: MaybeRefOrGetter<string | null | undefined>) { | ||||||||||||||||||||||||||||||||||||||||||||
| return useAsyncData<LicenseChangesResult>( | ||||||||||||||||||||||||||||||||||||||||||||
| () => `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<NpmRegistryResponse>(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[] | ||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should only compare to the last version, there is some logic here that could be reused (you can move it into its own utility file in shared too npmx.dev/app/composables/useInstallSizeDiff.ts Lines 19 to 39 in 1dd1be9
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Sort versions ascending to compare chronologically | ||||||||||||||||||||||||||||||||||||||||||||
| versions.sort((a, b) => { | ||||||||||||||||||||||||||||||||||||||||||||
| const dateA = new Date(data.time[a.version] as string).getTime() | ||||||||||||||||||||||||||||||||||||||||||||
| const dateB = new Date(data.time[b.version] as string).getTime() | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Ascending order (oldest to newest) | ||||||||||||||||||||||||||||||||||||||||||||
| return dateA - dateB | ||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // 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)], | ||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||

Uh oh!
There was an error while loading. Please reload this page.