Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3a0543f
1826-add a warning and info for license changes. Added support for TR…
aisiklar Mar 21, 2026
bfd9550
[autofix.ci] apply automated fixes
autofix-ci[bot] Mar 21, 2026
fa52bff
fix:warning style changed and unintended chars erased
aisiklar Mar 24, 2026
bd4ebe5
[autofix.ci] apply automated fixes
autofix-ci[bot] Mar 24, 2026
9afe21d
fix:added TR language support for warning text and changed logic for …
aisiklar Mar 24, 2026
ecbb660
[autofix.ci] apply automated fixes
autofix-ci[bot] Mar 24, 2026
b91aab5
fix: removed the vue-118n import and used autoimported
aisiklar Mar 24, 2026
34b84c5
Merge branch 'main' into feature/1826_license_changes
ghostdevv Mar 25, 2026
302952a
fix:feature/1826-warning message moved to proper place, license chang…
aisiklar Apr 2, 2026
45e2528
test added
aisiklar Apr 3, 2026
e4fd978
removed the unneeded watcher
aisiklar Apr 3, 2026
a5b4e09
Merge remote-tracking branch 'origin/main' into feature/1826_license_…
ghostdevv Apr 4, 2026
61432b3
moved license change logic to server side, to an api, changed nested …
aisiklar Apr 9, 2026
ae2ac2a
changes to translations and schema from prev merge
aisiklar Apr 9, 2026
18a58a1
chore: update i18n
ghostdevv Apr 9, 2026
ef3d106
Merge remote-tracking branch 'origin/main' into feature/1826_license_…
ghostdevv Apr 9, 2026
0fac1ce
fix(i18n): gen schema
ghostdevv Apr 9, 2026
c3b5986
chore: fmt
ghostdevv Apr 9, 2026
e1a28da
Update server/api/registry/license-change/[...pkg].get.ts
aisiklar Apr 10, 2026
083e17f
fix: compute license changes in page
43081j Apr 10, 2026
b397996
Update i18n/locales/en.json
aisiklar Apr 13, 2026
1cd6227
Update i18n/locales/en.json
aisiklar Apr 13, 2026
4b2d0b9
TR translation added, with suggested changes on the warning message
aisiklar Apr 13, 2026
db9c4ed
custom key added to useFetch function in useLicenseChange, comments u…
aisiklar Apr 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion app/components/LicenseDisplay.vue
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'
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

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('; '),
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

const hasAnyValidLicense = computed(() => tokens.value.some(t => t.type === 'license' && t.url))

Check warning on line 31 in app/components/LicenseDisplay.vue

View workflow job for this annotation

GitHub Actions / 🤖 Autofix code

eslint(no-shadow)

't' is already declared in the upper scope.
</script>

<template>
Expand All @@ -21,7 +42,7 @@
class="link-subtle"
:title="$t('package.license.view_spdx')"
>
{{ token.value }}
{{ token.value }}!!
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
</a>
<span v-else-if="token.type === 'license'">{{ token.value }}</span>
<span v-else-if="token.type === 'operator'" class="text-4xs">{{ token.value }}</span>
Expand All @@ -32,4 +53,26 @@
aria-hidden="true"
/>
</span>
<div v-if="changes.length > 0" class="text-red-500 flex justify-start items-center gap-x-1">
<p>change!</p>
<TooltipApp interactive position="top">
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
<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>
80 changes: 80 additions & 0 deletions app/composables/useLicenseChanges.ts
Original file line number Diff line number Diff line change
@@ -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<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[]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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

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)
// Don't compare the second version against the first as the first
// has no baseline so a large size difference is expected
if (currentIdx <= 1) return null
return stableVersions[currentIdx - 1]!
}


// Sort versions ascending to compare chronologically
versions.sort((a, b) => {
const parse = (v: string) => v.split('.').map(Number)

Check warning on line 53 in app/composables/useLicenseChanges.ts

View workflow job for this annotation

GitHub Actions / 🤖 Autofix code

eslint-plugin-unicorn(consistent-function-scoping)

Function `parse` does not capture any variables from its parent scope
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!
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
})

// 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)],
},
)
}
6 changes: 5 additions & 1 deletion app/pages/package/[[org]]/[name].vue
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,11 @@ const showSkeleton = shallowRef(false)
{{ $t('package.stats.license') }}
</dt>
<dd class="font-mono text-sm text-fg">
<LicenseDisplay v-if="pkg.license" :license="pkg.license" />
<LicenseDisplay
v-if="pkg.license"
:license="pkg.license"
:package-name="pkg.name"
/>
<span v-else>{{ $t('package.license.none') }}</span>
</dd>
</div>
Expand Down
3 changes: 3 additions & 0 deletions i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
3 changes: 3 additions & 0 deletions i18n/locales/tr-TR.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
9 changes: 9 additions & 0 deletions i18n/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
Loading