-
-
Notifications
You must be signed in to change notification settings - Fork 394
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 all commits
3a0543f
bfd9550
fa52bff
bd4ebe5
9afe21d
ecbb660
b91aab5
34b84c5
302952a
45e2528
e4fd978
a5b4e09
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 |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| <script setup lang="ts"> | ||
| import { useLicenseChanges } from '~/composables/useLicenseChanges' | ||
|
|
||
| const props = defineProps<{ | ||
| license?: string | ||
| packageName?: string | ||
| resolvedVersion: string | null | undefined | ||
| }>() | ||
|
|
||
| const licenseChanges = useLicenseChanges( | ||
| () => props.packageName, | ||
| () => props.resolvedVersion, | ||
| ) | ||
|
|
||
| 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('; '), | ||
| ) | ||
| </script> | ||
|
|
||
| <template> | ||
| <div | ||
| v-if="changes && changes.length > 0" | ||
| class="border border-amber-600/40 bg-amber-500/10 rounded-lg mt-1 gap-x-1 py-2 px-3" | ||
| :aria-label="$t('package.versions.license_change_help')" | ||
| > | ||
| <p class="text-md text-amber-800 dark:text-amber-400 flex items-center gap-2"> | ||
| <span | ||
| class="i-lucide:alert-triangle w-4 h-4 flex-shrink-0" | ||
| role="img" | ||
| :aria-label="$t('package.versions.license_change_help')" | ||
| /> | ||
| {{ $t('package.versions.license_change_warning') }} | ||
| </p> | ||
| <p class="text-md text-amber-800 dark:text-amber-400 mt-1"> | ||
| <i18n-t keypath="package.versions.changed_license" tag="span"> | ||
| <template #license_change>{{ licenseChangeText }}</template> | ||
| </i18n-t> | ||
| </p> | ||
| </div> | ||
| </template> | ||
|
|
||
| <style scoped></style> |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,88 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| 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>, | ||||||||||||||||||||||||||||||||||||||||||||
| resolvedVersion: MaybeRefOrGetter<string | null | undefined> = () => undefined, | ||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||
| return useAsyncData<LicenseChangesResult>( | ||||||||||||||||||||||||||||||||||||||||||||
| () => { | ||||||||||||||||||||||||||||||||||||||||||||
| const name = toValue(packageName) | ||||||||||||||||||||||||||||||||||||||||||||
| const version = toValue(resolvedVersion) ?? 'latest' | ||||||||||||||||||||||||||||||||||||||||||||
| return `license-changes:${name}:${version}` | ||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||
| async () => { | ||||||||||||||||||||||||||||||||||||||||||||
| const name = toValue(packageName) | ||||||||||||||||||||||||||||||||||||||||||||
| const resolvedVer = toValue(resolvedVersion) | ||||||||||||||||||||||||||||||||||||||||||||
| 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[] = [] | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // `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 | ||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // When resolvedVer is not provided, check changes across all versions | ||||||||||||||||||||||||||||||||||||||||||||
| const targetVersion = resolvedVer ?? versions[versions.length - 1]?.version | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if (targetVersion) { | ||||||||||||||||||||||||||||||||||||||||||||
| const resolvedIndex = versions.findIndex(v => v.version === targetVersion) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if (resolvedIndex > 0) { | ||||||||||||||||||||||||||||||||||||||||||||
| const currentLicense = (versions[resolvedIndex]?.license as string) ?? 'UNKNOWN' | ||||||||||||||||||||||||||||||||||||||||||||
| const previousLicense = (versions[resolvedIndex - 1]?.license as string) ?? 'UNKNOWN' | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if (currentLicense !== previousLicense) { | ||||||||||||||||||||||||||||||||||||||||||||
| changes.push({ | ||||||||||||||||||||||||||||||||||||||||||||
| from: previousLicense, | ||||||||||||||||||||||||||||||||||||||||||||
| to: currentLicense, | ||||||||||||||||||||||||||||||||||||||||||||
| version: (versions[resolvedIndex]?.version as string) || 'UNKNOWN', | ||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| return { changes } | ||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||
| default: () => ({ changes: [] }), | ||||||||||||||||||||||||||||||||||||||||||||
| watch: [() => toValue(packageName), () => toValue(resolvedVersion)], | ||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -432,7 +432,11 @@ | |
| "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}", | ||
|
Comment on lines
+436
to
+438
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. Currently it's nesting the keys right? I think they should be one key, so it'd be like
Author
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. agree on that. thanks, as we changed the logic from checking all versions to last version, there is really no meaning to show it anymore, even if it runs the logic on resolvedVersion, which user can change by selecting from version selector. |
||
| "license_change_warning": "License change!", | ||
| "filter_tooltip_link": "semver range", | ||
| "no_matches": "No versions match this range", | ||
| "copy_alt": { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, another thing here - it may be worth doing this on the server so we don't have to fetch the packument on the client. I think I linked this before, but it's also had some improvements since then. Let me know if you want some help, or don't understand!
https://github.com/npmx-dev/npmx.dev/blob/064cf97ebc89136a267a0c44d757bfaf69212b04/app/composables/useInstallSizeDiff.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, i am not familiar with nuxt (working with next.js, react and vue, mostly) but i will check this example in detail and run my composable on server. If i have some questions i will return you. Thanks for pointing this out to me.