Draft: Move main.js Vue apps to Vue 3 behind a flag

What does this MR do and why?

This is step 3 of 3. It moves every Vue app that main.js mounts to Vue 3 (@vue/compat) behind the vue3_migrate_main flag. There is no bundler config change.

main.js runs on every page, and its Vue apps are locked to Vue 2. The vue3_migration.yml mechanism cannot switch them, because main is prepended to every page entry and has no bundle of its own. A standalone main entry would fix that, but it changes the chunk graph of every page.

This MR uses Option 2 of the Vue 3 migration guide instead:

  • main_app.js holds the Vue apps that main.js mounts:
    • behaviors
    • user and work item attribute popovers
    • the version check badge and security patch modal
    • the session expiry modal
    • the panel resizers
    • the EE trial banner and SAML reload modal
  • With the flag on, main.js imports main_app.js with ?vue3. If that import fails, the Vue 2 copy runs and the error goes to Sentry.
  • With the flag off, main.js calls the Vue 2 copy synchronously. The deferred apps still run in deferredInitialisation(), grouped together after initPanelHeightCalc().
  • main_app.js calls setGlTooltipDefaultContainer itself, because the Vue 3 copy of @gitlab/ui has its own config.
  • Gitlab::GonHelper#add_gon_feature_flags pushes the flag with the current_user actor, because main.js runs on every page.

What the earlier steps provide

  • !261497 (closed): the static Vue 2 import of main_app.js is evaluated even when the flag is on. After step 1, nothing below it does work on import, so the Vue 2 copy stays idle.
  • !261499 (merged): modules that main.js and Vue 2 page code both use stay on one Vue 2 copy. Two of them matter here:
    • tooltips: work item attribute popovers call its dispose() through ~/issuable/popover, and with one copy that call reaches the real tooltip.
    • shortcuts: one copy means every key is bound once.

How the move was checked

  • I simulated the change with the infection scanner's lane walk and the loader's real isInfectable rule:
    • Vue 2 lane: main.js without main_app.js, every page entry, and the always-loaded bundles
    • Vue 3 lane: main_app.js
  • These modules also hold module state and get two copies. They were triaged by hand as harmless:
    • emoji (a cache)
    • issuable/popover (a debounce timer; the mounted flag lives in the DOM)
    • text_markdown (an IME flag per listener)
    • apollo_captcha_link (a stateless link)
    • internal_events (a bind guard)
  • A Babel pass listed every module in the Vue 3 lane that runs code on import. Only module-local assignments remain: gfm_auto_complete in CE and EE, markdown/schema.js, and vue3compat/vue.js, which is per runtime by design.
  • The state detection is pattern-based, so treat it as a lower bound. Feature specs enable every flag, so the MR pipeline runs all of them with this flag on.

Result

  • About 49 components move to Vue 3.
  • 115 components below main_app.js stay on Vue 2. All of them are held back by the existing behaviors/preview_markdown.js blocklist entry (glql, content_editor and others), as on rollout pages today.
  • The Vue 2 runtime stays on every page, because ~/commons and Vue 2 page code still need it.

What was ruled out

  • One app at a time. It never reaches "main on Vue 3".
  • A switch inside the bundle, with static require of both copies. It ships both copies to every page and does not work under Vite.
  • A main.vue3 entry. It needs the standalone main entry above, or about 2,500 page entries.

Screenshots or screen recordings

Not recorded yet.

How to set up and validate locally

  1. Enable the flag in the Rails console:

    Feature.enable(:vue3_migrate_main)
  2. Open a project page, such as http://127.0.0.1:3000/gitlab-org/gitlab-test.

  3. In the console, check for [gitlab] [V] Using Vue.js 3 (with @vue/compat) for PanelWidthResizerRoot.

  4. Run document.querySelectorAll('[data-gitlab-vue3-app]'). It returns the panel resizer roots.

  5. Check that nothing runs twice:

    • Press ?. One shortcuts help modal opens.
    • Hover a link with a tooltip, then click it. The tooltip hides.
    • Click a collapsible section toggle. It opens once.
  6. Hover a user link. The user popover opens.

  7. Disable the flag and reload. The console shows no Vue 3 notice for these roots.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

References

Edited by Miguel Rincon

Merge request reports

Loading
Loading