Skip to content

feat: support alias package#63

Merged
9romise merged 7 commits intomainfrom
feat/alias-package
Mar 7, 2026
Merged

feat: support alias package#63
9romise merged 7 commits intomainfrom
feat/alias-package

Conversation

@9romise
Copy link
Member

@9romise 9romise commented Mar 4, 2026

Support alias packages like npm:nuxt@~4.3.0

This PR also tries to resolve JSR packages, see: https://jsr.io/docs/npm-compatibility

9romise added 5 commits March 4, 2026 10:29
# Conflicts:
#	src/providers/diagnostics/rules/replacement.ts
#	src/providers/diagnostics/rules/upgrade.ts
#	src/providers/diagnostics/rules/vulnerability.ts
#	src/utils/package.ts
#	tests/utils/package.test.ts
@9romise 9romise marked this pull request as ready for review March 6, 2026 18:10
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 6, 2026

📝 Walkthrough

Walkthrough

This PR adds package-name resolution and JSR-aware handling across utilities and providers. It extends version parsing with an aliasName, adds helpers (resolvePackageName, isJsrNpmPackage, jsrNpmToJsrName), and threads a resolved name field through DiagnosticContext and many diagnostic rules. Completion, hover and diagnostics providers now resolve and use the derived package name when fetching package info and building URLs. Tests updated to cover new utilities and alias/JSR parsing behaviour.

Possibly related PRs

  • npmx-dev/vscode-npmx PR 36 — Modifies version utilities and the upgrade diagnostic rule; overlaps in parsing/formatting and upgrade-rule signature changes.
  • npmx-dev/vscode-npmx PR 46 — Alters the diagnostics upgrade rule (message and metadata); directly related to the upgrade-rule refactor in this PR.
  • npmx-dev/vscode-npmx PR 30 — Changes version-protocol parsing and validation used by providers; closely related to the alias/protocol handling added here.
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description accurately relates to the changeset, which implements support for alias packages and JSR package resolution through npm compatibility.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/alias-package

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.

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/providers/diagnostics/rules/upgrade.ts (1)

28-29: ⚠️ Potential issue | 🟡 Minor

Compare against the raw dist-tag, not exactVersion.

exactVersion is already the resolved semver by this point, so this condition will not fire for normal tag-based inputs such as latest or next. The rule still emits upgrade hints for dist-tag specs instead of letting checkDistTag own that case.

Minimal fix
-  if (Object.hasOwn(pkg.distTags, exactVersion))
+  if (Object.hasOwn(pkg.distTags, parsed.version))
     return
🧹 Nitpick comments (2)
src/providers/completion-item/version.ts (1)

37-40: Unreachable guard: resolvePackageName always returns a string.

Per src/utils/package.ts:19-21, resolvePackageName returns parsed?.aliasName ?? depName, which always yields a non-empty string since depName (from info.name) is guaranteed to be truthy. The if (!packageName) check is therefore unreachable.

This is harmless defensive code, but you could simplify by removing the guard.

🔧 Optional simplification
     const packageName = resolvePackageName(name, parsed)
-    if (!packageName)
-      return
-
     const pkg = await getPackageInfo(packageName)
src/utils/package.ts (1)

29-38: Consider defensive handling for malformed JSR npm names.

The function correctly transforms @jsr/scope__name@scope/name. However, when no __ separator is found (lines 35-36), it returns a bare name like 'something', which is invalid for JSR (all JSR packages are scoped).

Since this edge case shouldn't occur with valid JSR npm packages, this is low priority. Optionally, you could return null or throw to surface malformed input early.

🛡️ Optional defensive handling
   const bare = name.slice(JSR_NPM_SCOPE.length)
   const separatorIndex = bare.indexOf('__')
   if (separatorIndex === -1)
-    return bare
+    return bare // Note: bare names without scope are invalid for JSR.io
   return `@${bare.slice(0, separatorIndex)}/${bare.slice(separatorIndex + 2)}`

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c185daab-8014-47af-a6b7-0829dfc3d395

📥 Commits

Reviewing files that changed from the base of the PR and between 972db01 and f0b050b.

📒 Files selected for processing (15)
  • playground/package.json
  • src/providers/completion-item/version.ts
  • src/providers/diagnostics/index.ts
  • src/providers/diagnostics/rules/deprecation.ts
  • src/providers/diagnostics/rules/dist-tag.ts
  • src/providers/diagnostics/rules/engine-mismatch.ts
  • src/providers/diagnostics/rules/replacement.ts
  • src/providers/diagnostics/rules/upgrade.ts
  • src/providers/diagnostics/rules/vulnerability.ts
  • src/providers/hover/npmx.ts
  • src/utils/package.ts
  • src/utils/version.ts
  • tests/diagnostics/context.ts
  • tests/utils/package.test.ts
  • tests/utils/version.test.ts

Comment on lines +45 to +54
if (protocol === 'npm') {
const lastAtIndex = version.lastIndexOf('@')
if (lastAtIndex > 0) {
aliasName = version.substring(0, lastAtIndex)
version = version.substring(lastAtIndex + 1)

if (isJsrNpmPackage(aliasName)) {
aliasName = jsrNpmToJsrName(aliasName)
protocol = 'jsr'
}
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

🏁 Script executed:

cd /tmp && find . -name "version.ts" -type f 2>/dev/null | head -20

Repository: npmx-dev/vscode-npmx

Length of output: 46


🏁 Script executed:

git ls-files | grep -E "(version\.ts|npmx\.ts)" | head -20

Repository: npmx-dev/vscode-npmx

Length of output: 223


🏁 Script executed:

git ls-files src/utils/ | head -20

Repository: npmx-dev/vscode-npmx

Length of output: 288


🏁 Script executed:

cat -n src/utils/version.ts | head -80

Repository: npmx-dev/vscode-npmx

Length of output: 2844


🏁 Script executed:

cat -n src/utils/version.ts | tail -40

Repository: npmx-dev/vscode-npmx

Length of output: 1354


🏁 Script executed:

cat -n src/utils/package.ts | grep -A 10 "isJsrNpmPackage\|jsrNpmToJsrName"

Repository: npmx-dev/vscode-npmx

Length of output: 670


🏁 Script executed:

cat -n src/providers/completion-item/version.ts | head -50

Repository: npmx-dev/vscode-npmx

Length of output: 1881


🏁 Script executed:

cat -n src/providers/document-link/npmx.ts | head -50

Repository: npmx-dev/vscode-npmx

Length of output: 2311


🏁 Script executed:

cat -n src/providers/document-link/npmx.ts | sed -n '47,120p'

Repository: npmx-dev/vscode-npmx

Length of output: 2415


🏁 Script executed:

cat -n src/utils/package.ts | head -50

Repository: npmx-dev/vscode-npmx

Length of output: 1856


🏁 Script executed:

grep -r "isSupportedProtocol\|formatUpgradeVersion" src/ --include="*.ts" -B 2 -A 2 | head -80

Repository: npmx-dev/vscode-npmx

Length of output: 5593


🏁 Script executed:

cat -n src/utils/version.ts | sed -n '1,60p'

Repository: npmx-dev/vscode-npmx

Length of output: 2209


🏁 Script executed:

find . -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "parseVersion\|jsrNpmPackage" 2>/dev/null | head -5

Repository: npmx-dev/vscode-npmx

Length of output: 93


🏁 Script executed:

rg "jsr:" -A 3 -B 3 --type ts | head -60

Repository: npmx-dev/vscode-npmx

Length of output: 908


🏁 Script executed:

cat -n tests/utils/version.test.ts

Repository: npmx-dev/vscode-npmx

Length of output: 5515


🏁 Script executed:

cat -n src/utils/package.ts | sed -n '19,42p'

Repository: npmx-dev/vscode-npmx

Length of output: 922


🏁 Script executed:

rg "npm-compatible\|jsr.*npm\|@jsr/" --type ts -i -B 2 -A 2 | head -100

Repository: npmx-dev/vscode-npmx

Length of output: 46


🏁 Script executed:

rg "parseVersion\|formatUpgradeVersion" src/providers -A 3 -B 1 | grep -A 3 "isSupportedProtocol" | head -40

Repository: npmx-dev/vscode-npmx

Length of output: 46


🏁 Script executed:

cat -n src/utils/resolve.ts

Repository: npmx-dev/vscode-npmx

Length of output: 1968


Rewrite npm-compatible JSR aliases to preserve the npm protocol rather than converting to jsr.

When parsing npm:@jsr/luca__cases@^1.0.1, the code rewrites the protocol from npm to jsr after transforming the alias name to @luca/cases (lines 51–53). This causes the parsed result to have protocol: 'jsr', which is in the UNSUPPORTED_PROTOCOLS set, so downstream callers like completion and document-link providers that check isSupportedProtocol(parsed.protocol) skip processing these dependencies entirely.

Additionally, formatUpgradeVersion() would then output jsr:@luca/cases@version instead of the original npm:@jsr/luca__cases@version, losing the ability to round-trip the original spec.

Preserve the npm protocol and alias target during parsing, and delegate canonicalisation of the package identity (mapping @jsr/luca__cases@luca/cases and determining the final registry) to a separate resolver step when needed for metadata or link resolution.

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: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 70355416-7033-44e6-88d0-c710040b0d70

📥 Commits

Reviewing files that changed from the base of the PR and between f0b050b and 2646de3.

📒 Files selected for processing (2)
  • src/providers/diagnostics/rules/upgrade.ts
  • tests/utils/package.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/utils/package.test.ts

Comment on lines +28 to 29
if (Object.hasOwn(pkg.distTags, dep.version))
return
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

Alias dist-tags are no longer filtered out.

Line 28 now checks pkg.distTags against dep.version, but DependencyInfo.version is the raw manifest spec (src/types/extractor.ts:8-13). For alias declarations like npm:react@next or npm-compatible JSR aliases, that raw value will never equal a dist-tag key such as next, so this early return is skipped and we emit a bogus upgrade hint. Please normalise the declared spec down to its tag component before calling hasOwn, or read the tag from parsed instead.

@9romise 9romise merged commit 29168b6 into main Mar 7, 2026
11 checks passed
@9romise 9romise deleted the feat/alias-package branch March 7, 2026 03:02
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.

1 participant