Skip to content

fix: handle scoped npm package aliases SvelteSource.resolveAlias#112

Open
LucHeart wants to merge 2 commits into
WarningImHack3r:mainfrom
LucHeart:main
Open

fix: handle scoped npm package aliases SvelteSource.resolveAlias#112
LucHeart wants to merge 2 commits into
WarningImHack3r:mainfrom
LucHeart:main

Conversation

@LucHeart
Copy link
Copy Markdown

When using something like turborepo in my case, you end up with scoped paths like @repo/yourPackageName/components/
The previous path resolver always cut this of at the first slash, which usually works if you have something like $lib/components/

We now handle scoped namespaces separate,
Not sure if that is also an issue with other frameworks, but this solves it for Svelte.

PS: The workflow change is kinda forced, since I needed to push again after enabling actions to trigger a CI build.
So I was like, why not add a dispatch trigger while am at it, hope you dont mind.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 30, 2026

Walkthrough

This pull request introduces two changes. First, a workflow_dispatch trigger is added to the GitHub Actions workflow file, enabling manual execution of the build workflow alongside existing push and pull request triggers. Second, the resolveAlias method in the SvelteSource module is updated to improve handling of @-scoped package aliases containing slashes, by splitting the alias into prefix and suffix components and using wildcard fallback patterns when querying tsconfig paths configuration.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main fix: handling scoped npm package aliases in SvelteSource.resolveAlias, which aligns with the primary change in the changeset.
Description check ✅ Passed The description is directly related to the changeset, explaining the problem with scoped package paths and the solution, plus clarifying the secondary workflow change.

✏️ 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.

Copy link
Copy Markdown

@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

🧹 Nitpick comments (2)
.github/workflows/build.yml (1)

9-9: Update comment to reflect the new trigger.

The comment now needs updating to mention the workflow_dispatch trigger added below.

📝 Suggested documentation update
-# The workflow is triggered on push and pull_request events.
+# The workflow is triggered on push, pull_request, and manual workflow_dispatch events.
src/main/kotlin/com/github/warningimhack3r/intellijshadcnplugin/backend/sources/impl/SvelteSource.kt (1)

87-96: Please add regression coverage for alias-prefix selection.

This block now needs to preserve $lib/..., single-segment @..., and scoped @scope/pkg/... resolution across both exact and wildcard paths entries. A focused test matrix here would make this kind of parsing change much safer.

Based on learnings: In SvelteSource alias resolution, SvelteKit uses both exact alias keys (e.g., "$lib") and wildcard keys (e.g., "$lib/*"), so the current lookup approach handles both patterns correctly. The path values in tsconfig are consistently arrays, not strings.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 36c711b2-e725-49af-8b71-eac6e6ebcd82

📥 Commits

Reviewing files that changed from the base of the PR and between 89cfe2f and 360ba96.

📒 Files selected for processing (2)
  • .github/workflows/build.yml
  • src/main/kotlin/com/github/warningimhack3r/intellijshadcnplugin/backend/sources/impl/SvelteSource.kt

Comment on lines +78 to 96
val (aliasPrefix, suffix) = if (alias.startsWith("@")) {
val parts = alias.split("/", limit = 3)
val prefix = if (parts.size >= 2) "${parts[0]}/${parts[1]}" else alias
val rest = if (parts.size >= 3) parts[2] else ""
prefix to rest
} else {
alias.substringBefore("/") to alias.substringAfter("/", "")
}

val paths = parseTsConfig(tsConfig)
.asJsonObject?.get("compilerOptions")
?.asJsonObject?.get("paths")
?.asJsonObject?.get(alias.substringBefore("/"))

val aliasPath = (paths?.asJsonObject?.get(aliasPrefix)
?: paths?.asJsonObject?.get("$aliasPrefix/*"))
?.asJsonArray?.get(0)
?.asJsonPrimitive?.content ?: throw Exception("Cannot find alias $alias in $tsConfig")
val normalized = aliasPath.replace(Regex("^\\.+/"), "")
val suffix = alias.substringAfter("/", "")
val normalized = aliasPath.replace(Regex("^\\.+/"), "").removeSuffix("/*")
val resolved = if (suffix.isEmpty()) normalized else "$normalized/$suffix"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

This regresses single-segment @ aliases.

The new branch treats every @... import as @scope/package/.... That breaks previously supported aliases like @/components/ui/button and @components/foo, because the resolver now looks for @/components/* or @components/foo/* instead of falling back to the original single-segment key (@/*, @components/*, etc.).

🛠️ One way to preserve both scoped and non-scoped forms
-        val (aliasPrefix, suffix) = if (alias.startsWith("@")) {
-            val parts = alias.split("/", limit = 3)
-            val prefix = if (parts.size >= 2) "${parts[0]}/${parts[1]}" else alias
-            val rest = if (parts.size >= 3) parts[2] else ""
-            prefix to rest
-        } else {
-            alias.substringBefore("/") to alias.substringAfter("/", "")
-        }
+        val defaultCandidate = alias.substringBefore("/") to alias.substringAfter("/", "")
+        val scopedCandidate = alias
+            .takeIf { it.startsWith("@") }
+            ?.split("/", limit = 3)
+            ?.takeIf { it.size >= 2 && it[0] != "@" }
+            ?.let { "${it[0]}/${it[1]}" to it.getOrElse(2) { "" } }
 
         val paths = parseTsConfig(tsConfig)
             .asJsonObject?.get("compilerOptions")
             ?.asJsonObject?.get("paths")
 
-        val aliasPath = (paths?.asJsonObject?.get(aliasPrefix)
-            ?: paths?.asJsonObject?.get("$aliasPrefix/*"))
-            ?.asJsonArray?.get(0)
-            ?.asJsonPrimitive?.content ?: throw Exception("Cannot find alias $alias in $tsConfig")
-        val normalized = aliasPath.replace(Regex("^\\.+/"), "").removeSuffix("/*")
-        val resolved = if (suffix.isEmpty()) normalized else "$normalized/$suffix"
+        fun resolveCandidate(aliasPrefix: String, suffix: String): String? {
+            val aliasPath = (paths?.asJsonObject?.get(aliasPrefix)
+                ?: paths?.asJsonObject?.get("$aliasPrefix/*"))
+                ?.asJsonArray?.get(0)
+                ?.asJsonPrimitive?.content ?: return null
+
+            val normalized = aliasPath.replace(Regex("^\\.+/"), "").removeSuffix("/*")
+            return if (suffix.isEmpty()) normalized else "$normalized/$suffix"
+        }
+
+        val resolved = (scopedCandidate?.let { (aliasPrefix, suffix) ->
+            resolveCandidate(aliasPrefix, suffix)
+        } ?: resolveCandidate(defaultCandidate.first, defaultCandidate.second))
+            ?: throw Exception("Cannot find alias $alias in $tsConfig")

Copy link
Copy Markdown
Owner

@WarningImHack3r WarningImHack3r left a comment

Choose a reason for hiding this comment

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

Thanks a LOT for your contribution! Could you also bring that to other implementations once we're fixed on this one?

# Trigger the workflow on any pull request
pull_request:
# Allow manual trigger
workflow_dispatch:
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I'm not particularly a fan of that as the use cases are really rare, but it doesn't hurt either I guess

val aliasPath = parseTsConfig(tsConfig)
// Handle scoped package aliases (e.g. @repo/ui/components).
// For @-prefixed aliases, the package name includes a slash (@scope/name),
// so we split into at most 3 parts to correctly separate the prefix from the rest.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

At first glance, splitting with exact numbers feels wonky, CodeRabbit review seems right on the kind of problem that could arise.

But to be honest, I'm not really sure what's the best way to handle this: maybe looking at what the original shadcn implementation looks like could help?

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.

2 participants