Skip to content

feat: add a warning when the package license changes#2188

Open
aisiklar wants to merge 2 commits intonpmx-dev:mainfrom
aisiklar:feature/1826_license_changes
Open

feat: add a warning when the package license changes#2188
aisiklar wants to merge 2 commits intonpmx-dev:mainfrom
aisiklar:feature/1826_license_changes

Conversation

@aisiklar
Copy link

🔗 Linked issue

fixes #1826

🧭 Context

added info/warning beneath the license info, "changed!" if there is a change in the license with additional tooltip which shows info about the change, similar to "from x to y at version a.b".
Also modified the Turkish language support too.

📚 Description

as mentioned in the issue, sometimes license changes and it is important to notify the potential user of the package. This pull request is done to make sure that if there is a change in the license the user will be able to see it and also will get info about the details of the change (like from x license to y license and at version a.b.c)

The following changes are done to the codebase:
modified the [name].vue file: added a new props to LicenseDisplay component.
new composable, useLicenseChanges is written.
In the LicenseDisplay component, changes were made to use this composable and add new element for displaying info about the changed license.
Additionally, en.json and tr-TR.json and schema.json files are modified, to add i18n infrastructure and add Turkish language support (for other languages, additional changes are required)

before:
image

after:
image

image

@vercel
Copy link

vercel bot commented Mar 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Mar 21, 2026 11:27pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Mar 21, 2026 11:27pm
npmx-lunaria Ignored Ignored Mar 21, 2026 11:27pm

Request Review

@github-actions
Copy link

github-actions bot commented Mar 21, 2026

Lunaria Status Overview

🌕 This pull request will trigger status changes.

Learn more

By default, every PR changing files present in the Lunaria configuration's files property will be considered and trigger status changes accordingly.

You can change this by adding one of the keywords present in the ignoreKeywords property in your Lunaria configuration file in the PR's title (ignoring all files) or by including a tracker directive in the merged commit's description.

Tracked Files

File Note
i18n/locales/en.json Source changed, localizations will be marked as outdated.
i18n/locales/tr-TR.json Localization changed, will be marked as complete.
Warnings reference
Icon Description
🔄️ The source for this localization has been updated since the creation of this pull request, make sure all changes in the source have been applied.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 21, 2026

📝 Walkthrough

Walkthrough

This pull request introduces license change detection across npm package versions. A new composable useLicenseChanges fetches npm registry metadata, compares licence fields across semantic versions, and identifies transitions from one licence to another. The LicenseDisplay component integrates this composable, receives the package name as a prop, and renders a visual indicator with tooltip when licence changes are detected. The package detail page now passes the package name to LicenseDisplay. Internationalisation support is added with translated strings for English and Turkish, plus schema validation for the new translation keys.

Suggested reviewers

  • danielroe
  • userquin
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is directly related to the changeset, detailing the implementation of license change detection and UI indicators as specified in issue #1826.
Linked Issues check ✅ Passed The PR successfully implements all core requirements from issue #1826: detecting license changes across versions, displaying a visible indicator with 'changed!' label, and providing detailed tooltips showing the license transition and affected version.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the linked issue objectives. The modifications to LicenseDisplay, creation of useLicenseChanges composable, and i18n updates all serve the purpose of implementing license change detection and notification.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@aisiklar aisiklar mentioned this pull request Mar 21, 2026
3 tasks
@ghostdevv ghostdevv changed the title 1826-add a warning and info for license changes. Added support for TR language too. feat: add a warning when the package license changes Mar 21, 2026
Copy link
Contributor

@ghostdevv ghostdevv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you do the style similar to the package size increase warning that we have? #1673 Let me know if you have any questions or need any help!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f7c1c88e-8b86-4447-9fdb-bfbc10c1ff70

📥 Commits

Reviewing files that changed from the base of the PR and between 4294ada and bfd9550.

📒 Files selected for processing (6)
  • app/components/LicenseDisplay.vue
  • app/composables/useLicenseChanges.ts
  • app/pages/package/[[org]]/[name].vue
  • i18n/locales/en.json
  • i18n/locales/tr-TR.json
  • i18n/schema.json

:title="$t('package.license.view_spdx')"
>
{{ token.value }}
{{ token.value }}!!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove the unintended !! suffix from linked licence labels.

This renders values like MIT!!, which is incorrect and user-visible noise.

Suggested fix
-        {{ token.value }}!!
+        {{ token.value }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{{ token.value }}!!
{{ token.value }}

Comment on lines +56 to +58
<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">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Localise the change badge text instead of hard-coding English.

change! is currently not translated, so Turkish users still see English in this new UI element.

Suggested direction
-    <p>change!</p>
+    <p>{{ $t('package.versions.license_change_badge') }}</p>

Add package.versions.license_change_badge in locale files + schema.

Comment on lines +52 to +58
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!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In JavaScript, what does Number('0-beta') return, and how does Array.prototype.sort behave when the comparator returns NaN?

💡 Result:

Number('0-beta') returns NaN, because the Number constructor converts strings by parsing them as number literals, and parsing fails for invalid formats like '0-beta', resulting in NaN. Array.prototype.sort treats a comparator return value of NaN as equivalent to 0 (equal), per the ECMAScript specification: if the comparator returns NaN, it returns +0, meaning the elements are considered equal and their relative order is preserved (stable sort) or implementation-dependent if the comparator is inconsistent. Returning NaN from the comparator leads to undefined sort behavior across engines if not strictly equal comparisons.

Citations:


🏁 Script executed:

# First, let's look at the file structure and find the file
fd -t f "useLicenseChanges.ts" --max-one-result

Repository: npmx-dev/npmx.dev

Length of output: 294


🏁 Script executed:

# Check the actual code at lines 52-58
cat -n app/composables/useLicenseChanges.ts | sed -n '40,70p'

Repository: npmx-dev/npmx.dev

Length of output: 1465


🏁 Script executed:

# Search for NpmRegistryResponse interface definition
rg -A 10 "interface NpmRegistryResponse" app/composables/

Repository: npmx-dev/npmx.dev

Length of output: 599


🏁 Script executed:

# Check if there's a broader pattern of version handling in the codebase
rg "\.split\('\.'\)\.map\(Number\)" --type ts

Repository: npmx-dev/npmx.dev

Length of output: 880


🏁 Script executed:

# Look for the data source and how versions are fetched
rg -B 5 -A 15 "versions\.sort" app/composables/useLicenseChanges.ts

Repository: npmx-dev/npmx.dev

Length of output: 911


🏁 Script executed:

# Find NpmRegistryVersion type definition
rg -B 2 -A 8 "type NpmRegistryVersion|interface NpmRegistryVersion" app/composables/

Repository: npmx-dev/npmx.dev

Length of output: 758


🏁 Script executed:

# Check the full file to see all type definitions
cat -n app/composables/useLicenseChanges.ts | head -40

Repository: npmx-dev/npmx.dev

Length of output: 1293


🏁 Script executed:

# Check if there's test data showing what npm registry actually returns
fd -t f "\.json$" test/fixtures/npm-registry/packuments/ | head -5

Repository: npmx-dev/npmx.dev

Length of output: 337


🏁 Script executed:

# Look at a sample npm registry fixture to see if it has time field
cat test/fixtures/npm-registry/packuments/*/package.json 2>/dev/null | head -100

Repository: npmx-dev/npmx.dev

Length of output: 43


🏁 Script executed:

# Let's look at the structure of one fixture file
head -50 test/fixtures/npm-registry/packuments/@nuxt/kit.json | jq 'keys' 2>/dev/null || head -50 test/fixtures/npm-registry/packuments/@nuxt/kit.json

Repository: npmx-dev/npmx.dev

Length of output: 1475


🏁 Script executed:

# Check if any other code accesses time field from npm registry
rg "\.time\[|\.time\?" app/ server/ --type ts

Repository: npmx-dev/npmx.dev

Length of output: 1018


🏁 Script executed:

# Let's see a real npm registry response structure by checking what fields are in the fixture
python3 << 'EOF'
import json
try:
    with open('test/fixtures/npm-registry/packuments/@nuxt/kit.json', 'r') as f:
        data = json.load(f)
        # Show top-level keys
        print("Top-level keys:", list(data.keys())[:20])
        if 'time' in data:
            print("\ntime field exists:", type(data['time']))
            print("Sample time entries:", list(data['time'].items())[:3])
        if 'versions' in data:
            print("\nVersions count:", len(data['versions']))
            sample_version = next(iter(data['versions'].values()))
            print("Sample version structure:", {k: type(v).__name__ for k, v in list(sample_version.items())[:5]})
except Exception as e:
    print(f"Error: {e}")
EOF

Repository: npmx-dev/npmx.dev

Length of output: 559


🏁 Script executed:

# Check how versions are actually ordered in the fixture
python3 << 'EOF'
import json
try:
    with open('test/fixtures/npm-registry/packuments/@nuxt/kit.json', 'r') as f:
        data = json.load(f)
        versions = list(data['versions'].keys())
        print(f"Total versions: {len(versions)}")
        print(f"First 5: {versions[:5]}")
        print(f"Last 5: {versions[-5:]}")
        # Check for prerelease versions
        prerelease = [v for v in versions if '-' in v]
        print(f"Prerelease versions: {len(prerelease)}")
        if prerelease:
            print(f"Sample prereleases: {prerelease[:3]}")
except Exception as e:
    print(f"Error: {e}")
EOF

Repository: npmx-dev/npmx.dev

Length of output: 279


Add time field to NpmRegistryResponse interface and use chronological sorting by version release date.

The version sort comparator breaks with prerelease versions (e.g. 4.0.0-alpha.4 produces NaN when parsing 0-alpha). This causes unstable ordering, potentially reporting licence changes in the wrong sequence. The npm registry provides a time field mapping versions to ISO timestamps, which other parts of the codebase already use. Update the interface and sort logic to prioritise time-based ordering:

Suggested fix
interface NpmRegistryResponse {
  versions: Record<
    string,
    {
      version: string
      license?: string
    }
  >
+  time?: Record<string, string>
}

       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!
+        const aTime = data.time?.[a.version]
+        const bTime = data.time?.[b.version]
+        if (aTime && bTime) {
+          return new Date(aTime).getTime() - new Date(bTime).getTime()
+        }
+        return a.version.localeCompare(b.version, undefined, {
+          numeric: true,
+          sensitivity: 'base',
+        })
       })
🧰 Tools
🪛 GitHub Check: 🤖 Autofix code

[warning] 53-53: eslint-plugin-unicorn(consistent-function-scoping)
Function parse does not capture any variables from its parent scope

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

highlight license changes

2 participants