Force infection for pass-through modules above app roots

What does this MR do and why?

The infection scanner computes one infected flag, using an app-root barrier. A module whose only Vue-touching import is an app root is therefore marked clean, which is the correct answer to the upward question: the module holds no Vue itself.

createIsInfectable reuses that flag for the downward question, where the answer differs. When a Vue 3 module imports such a pass-through, the copy must be Vue 3. Because it is not, the importer resolves the clean copy, everything below it also resolves clean, and the subtree runs Vue 2 inside a page meant to run Vue 3. Every module-scope singleton down there becomes two objects, so the two halves of the page read different state.

On ee/app/assets/javascripts/pages/projects/issues/show:

V3  ee/app/assets/javascripts/pages/projects/issues/show/index.js
V3  app/assets/javascripts/issues/index.js
V2  app/assets/javascripts/issuable/index.js          <- clean sink
V2  app/assets/javascripts/sidebar/mount_sidebar.js
V2  app/assets/javascripts/batch_comments/store/index.js

Everything in that chain is already under one feature flag, so grouping apps under a shared flag does not fix it.

What this MR changes

  • Adds INFECTION_FORCELIST to config/helpers/context_aliases_shared.js: the four pass-through modules that a Vue 3 importer must resolve a Vue 3 copy of, so that infection continues past them rather than stopping.
  • Resolves and validates that list in config/helpers/vue3_infection_shared.js, next to createIsInfectable, its only caller. The constants module stays data only, because every webpack, vite, rspack and jest invocation requires it and so it must not touch the filesystem or throw.
  • createIsInfectable consults the list after the blocklist and before the graph. The blocklist wins, and a path on both lists fails at load rather than resolving by check order.
  • Entries are matched as exact repo-relative paths, not substrings. A path that does not exist fails at load, so a moved entry cannot become silently dead.
  • Entries for an edition the checkout lacks are dropped rather than validated: ee/ is absent from the FOSS mirror, jh/ from CE and EE checkouts.

mr_notes/mount_app.js is listed for a page that has no vue3_migration.yml yet. It is what unblocks that migration, so it is here rather than in the migration MR.

Measured effect

Simulated through the real createIsInfectable, against a scanner graph regenerated on this branch, on both CE (FOSS_ONLY=true) and EE resolution:

page without the list with the list
pages.projects.merge_requests.rapid_diffs 11 split singletons 0
pages.projects.merge_requests.show 11 0
ee/.../pages/projects/issues/show 6 0

No other migrated page's Vue 3 graph changes size, except issues/show, which grows from 790 to 863 modules.

How to verify

node scripts/frontend/infection_scanner/infection_scanner.mjs
FOSS_ONLY=true node scripts/frontend/infection_scanner/infection_scanner.mjs
yarn vitest:infection-scanner

!252671 (closed) runs the blocked migration on top of this branch, so its ~"pipeline::tier-3" result is the end-to-end check.

No changelog entry: build tooling, with no user-facing change until a page migration flag is enabled.

References

Edited by Miguel Rincon

Merge request reports

Loading
Loading