Generalize the Vue 3 infection-scanner alias-resolution fix

Summary

Generalizes the fix from work item 616988 / !250423 (merged) instead of leaving it as a one-off, per follow-up discussion there.

!250423 patched a build break where the Vue 3 infection scanner threw File not found in scanner data for vendor/vue-virtual-scroller, by adding a hardcoded path-substring exclude in both build plugins. Investigating whether other CONTEXT_ALIASES entries share the same bug class found that they do:

  • vue-virtual-scroll-list has the identical structural shape and is already imported by CI report widgets reachable from pages in active Vue 3 rollout (jobs/show, pipelines/show) -- a live-adjacent risk, not hypothetical.
  • vue-demi has the same shape but nothing imports it directly today (dormant).
  • vuedraggable is immune -- its alias target is a wholly different npm package.

Root cause: the scanner applies CONTEXT_ALIASES unconditionally while building its import graph, but the real Vite/webpack plugins only redirect through it inside an already-infected subtree. That asymmetry -- not anything specific to vue-virtual-scroller -- is what leaves "plain" resolutions permanently absent from the scanner's graph.

Separately, Jest has its own hand-duplicated copy of this same mapping, and it already drifted for real once (vue-demi was missing, causing Vue.util deprecation warnings in tests until manually patched).

What changed

  1. scripts/frontend/infection_scanner/analyze.mjs / infection_scanner.mjs: the scanner now resolves both the aliased (Vue 3 fork) target and the plain target for every specifier that exactly matches a CONTEXT_ALIASES key, giving the plain path a real graph node (infected: false) instead of leaving it absent. This reuses the existing "resolution alternatives" mechanism already used for pkg.exports/pkg.module multi-target packages, so no changes were needed to the infection-propagation logic itself.

    Along the way, fixed a related gap in resolveFile: it didn't consult a target directory's package.json (main/module) the way resolveNodeModuleAll does for real node_modules packages -- needed because the plain resolution of vendor/vue-virtual-scroller lands on a vendored package root whose real entry point is src/index.js, not a root-level index.js.

  2. jest.config.base.js: derives the Vue-3-mode moduleNameMapper from CONTEXT_ALIASES instead of hand-duplicating it, so this list can't silently drift again. @vue/compat and vuedraggable are deliberately excluded -- both need a different target under Jest's CJS require() runtime than the ESM/raw-import build Vite/webpack want, discovered by actually running Vue-3-mode Jest specs against the derived mapper (see Verification).

Not included here (dependency ordering): once !250423 merges, its vendor/vue-virtual-scroller-specific shouldExclude guards in vite_plugin_vue3_infection.mjs/webpack_vue3_infection_plugin.js, and the now-redundant regression test in vue3_infection_shared_spec.mjs, become dead code for their stated purpose and should be removed in a fast-follow. This MR is based on master, which doesn't have that guard yet, so there's nothing to remove here.

Verification

  • yarn vitest:infection-scanner run -- 124/124 passing (7 new tests covering the dual-resolution mechanism at both the resolver-unit and analyze() end-to-end level).
  • Regenerated tmp/infection_scanner.json locally and confirmed both previously-absent plain packages now appear with infected: false:
    • vendor/assets/javascripts/vue-virtual-scroller/src/index.js (and siblings)
    • node_modules/vue-virtual-scroll-list/dist/index.js
  • Ran real Vue-3-mode Jest specs against the derived moduleNameMapper (VUE_VERSION=3 yarn jest ...):
    • spec/frontend/super_sidebar/components/pinned_section_spec.js (exercises vuedraggable via the compat shim) -- this is what surfaced the @vue/compat/vuedraggable transform incompatibilities, now excluded.
    • spec/frontend/vue_shared/components/smart_virtual_list_spec.js and spec/frontend/ci/artifacts/components/artifacts_table_row_details_spec.js (exercise the two virtual-scroller mappings) -- pass.
    • Confirmed default Vue 2 mode (no VUE_VERSION set) is unaffected.
  • yarn eslint on all changed files -- clean.

References

Merge request reports

Loading
Loading