Repository navigation
fix(vue): an import through @/ or ~/ in a Vue project links the module's file - #2633
Merged
Merged
Conversation
…odule's file In a project with a `vue` dependency or a `.vue` file, a script's module import through `@/` or `~/` went through the Vue resolver's own mapping to `src/`, which linked the first node of the file it found: a constant the module declares, or a component. A tsconfig alias that starts like a Nuxt virtual module (`#app/where`) linked the importing file itself. Without Vue the import resolver links the module's file node, following tsconfig paths. So the walks a sync makes from a module or barrel that appears or goes away to the files importing it (#2512, #2560) never found these importers. Which node the edge hit also depended on the order a file's nodes were stored, so a sync and a fresh index could disagree. The Vue resolver now leaves a module import to the import resolver whenever that finds the file, and only adds what a Vue app's bundler finds from a plain script: a component named without `.vue`, or a folder's `index.vue` (`import Layout from '@/layout'`), linked to the file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In any project the Vue resolver detects (a
vue/nuxtdependency, or any.vuefile), everyimportsreference named by a module path went throughvueResolver.resolvebefore the import resolver did. For@/…and~/…, its Patterns 4 and 5 mapped the path tosrc/…by hand and returnedgetNodesInFile(path)[0]at 0.9 confidence. That is whichever node of the file came first, which is often not the file node.Fresh index of a fixture,
vuein package.json:@/flags/registry(from a.ts)REGISTERED_FLAGSregistry.ts@/table(from an SFC's<script setup>)COLUMNStable.ts~/utils/helperflaggedhelper.ts@/components/Foo.vue,@/layout#app/where(tsconfig#app/*→client/*)client/where.tsConsequences:
reopenReferencesThroughAddedFiles/…RemovedFilesand fix(sync): a lazy route relinks a page that appears below its barrel #2560'sreopenReferencesThroughBarrels. They followimportsedges into the file node (getImportsInto('file:…')), so in Vue projects they never found these importers. Found while validating fix(sync): a member reference through an import binding links its module when the module changes #2630, which had to work around it for member calls.[0]is depends on the order a file's nodes were stored, so a sync and a fresh index of the same tree could disagree: vue-realworld below.#app/…alias edge pointed at the importing file, so the module's dependents missed its importers.Fix
src/resolution/frameworks/vue.ts. For a module-path import (isJsPathImportRef):nulland let it link that file. It follows tsconfig paths, workspace packages and the same@//~/→src/fallbacks Patterns 4/5 hard-coded, with each language's extensions. A path that resolves only to a folder doesn't count (the context'sfileExistsis true for directories).@/and~/from a plain script, resolve the path the way an SFC's import would: thevueextension list, which adds.vueand/index.vue. Link that file node (import Layout from '@/layout'→src/layout/index.vue). This is the one thing Patterns 4/5 found that the import resolver doesn't from a.ts/.jsfile: what Vue CLI's bundler resolves.#imports,#components, …), unchanged, for modules no project file is.Patterns 4 and 5 (from the original Vue support, #66) are removed. No other resolver, no extraction and no schema change.
Validation
Tests.
__tests__/vue-module-imports.test.tshas 4 tests; all 4 fail on main (run in a detached worktree at baf1906):resolvedByisimportwherever the import resolver finds the file, andframeworkonly for@/layout'sindex.vue;src/components/index.ts:export const VERSION = 1; export * from './Button') is added equals a fresh index of the final tree;Button's edgesexact-match/importthe other way round from a fresh index.Mutations: dropping step 2 loses
@/layout's edge, and testing step 1 by a non-null path instead of a file node loses it too. Each fails the first two tests.Related suites (every
vue*,sync*, framework, import, alias, route and SFC test file): 72 files, 914 tests passed. Full suite: 7,251 passed, 0 failed, 82 skipped (Windows).Fresh-index A/B, main baf1906 against this PR, with the same kernel: Vue repos from the earlier kits, non-Vue controls. Edges that differ are paired by site (source, kind, line, col, refName):
.vuefilesframework→import; 12 component → file; 8 constant → fileframework→import; 48 component → file (index.vuefolders)framework→import; 14 component → file#/, not@/)Every changed edge is a module-path
importsedge re-pointed at the module's file node at the same site. Nodes and files are byte-identical on all 9 repos, and no edge was removed or added.Sync A/B: every 7th file held out, emptied, or deleted, then synced; each arm compared with its own fresh index.
vue-realworld's 4: on main,
@/common/jwt.servicelinked a constant in the fresh index but the file node after the sync, because the first node changed with the storage order. The other residues are line-for-line identical on both arms. vben is left out of the sync runs: its syncs fail withFOREIGN KEY constraint failedon main too (#2565, fix in #2574), and its fresh graph is unchanged.🤖 Generated with Claude Code