Skip to content

An import of a workspace package resolves onto a non-exported local variable of the same name (157 edges in vitejs/vite) #1719

Closed

Description

@bompus

On vitejs/vite @ 8492422, indexed at b9ca4b7, 157 cross-file imports edge rows resolve onto a single non-exported local variable.

Every import { defineConfig } from 'vite' across the playground and create-vite templates lands on:

playground/ssr-html/test-stacktrace.js::vite

which is this, at module scope in a file that exports nothing at all:

import { createServer } from 'vite'
// ...
const vite = await createServer({ /* ... */ })

The correct target is the workspace member packages/vite ("name": "vite", exports["."] = "./dist/node/index.js"), which is in the graph.

Counts

rows
edges targeting a node named vite 317
— contains (a file holding its own vite node; not at issue) 159
— imports, same-file 1
— imports, cross-file 157
distinct targets among those 157 1
source files importing from 'vite' 148 / 148

Why the existing guards do not catch it

matchByExactName filters candidates to non-import nodes that are lexically reachable, and commits when exactly one survives. Here one does — the module-level const — so the reference resolves with resolvedBy: 'exact-match' and confidence 0.9.

The bare-import guards do not apply, and correctly so. packages/vite/package.json declares the name vite and pnpm-workspace.yaml globs packages/*, so resolveWorkspaceImport('vite', …) reports the specifier project-local and isBoundToBareImport returns false. The specifier really is project-local; the failure is that the name then matches any node called vite rather than the member's entry.

Measured, on this corpus against the same merge base:

Two places a fix could go, and I do not have a view on which

  1. Export visibility. A module-level binding that the file does not export is not reachable by an import from another file. isLexicallyReachable admits it today. This looks like the general defect — it is not specific to workspace packages, and any non-exported top-level name is a candidate for any same-named import anywhere.
  2. Workspace package resolution. When a specifier resolves to a workspace member, the target should be that member's entry point rather than a name lookup. entryByName already exists for the ArkTS case.

(1) is the broader fix and would subsume this; (2) is narrower and would leave the same shape wherever a package is not a workspace member.

Reproduction

git clone https://github.com/vitejs/vite && cd vite && git checkout 8492422
codegraph init && codegraph index .

Then query the graph for imports edges whose target node is named vite and whose source file differs from the target's file.

Kernel rebuilt from source per arm; index db copied out before reading. Edge rows counted with multiplicity, keyed by source, target and kind — a distinct count reads lower because one source line can carry two genuine references at different columns.

Activity

  1. bompus commented on Sep 6, 2026

    @bompus
    ContributorAuthor

    Opened #1720 with the narrower of the two framings — not the workspace one, and not quite the export-visibility one either.

    The rule there is per-FILE rather than per-symbol: a JS/TS file with an import and no export of any form offers nothing to any other file. That avoids the two false-positive classes a per-symbol isExported test would hit (a later export { x }, and CommonJS), since neither sets the flag on the node.

    Measured on the same corpus and merge base: 320 rows removed, all 320 verified to land on a file that exports nothing, and 18 rows added that the patch is responsible for. The additions and a rejected stricter variant are written up in the PR rather than left out.

  2. colbymchenry commented on Sep 8, 2026

    @colbymchenry
    Owner

    Fixed in #1746.

    That PR lands @bompus’s sealed-module guard from #1720 onto current main (after #1745). #1745’s file-local visibility correctly reported LOST 0 / GAINED 0 on vite JS/TS and does not cover this case; the sealed-module rule does.

    Verified on Linux:

    Closing as fixed by #1746 (also Fixes #1719 on the PR for merge auto-close).

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions