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.jsholds the Vue apps thatmain.jsmounts:- 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.jsimportsmain_app.jswith?vue3. If that import fails, the Vue 2 copy runs and the error goes to Sentry. - With the flag off,
main.jscalls the Vue 2 copy synchronously. The deferred apps still run indeferredInitialisation(), grouped together afterinitPanelHeightCalc(). main_app.jscallssetGlTooltipDefaultContaineritself, because the Vue 3 copy of@gitlab/uihas its own config.Gitlab::GonHelper#add_gon_feature_flagspushes the flag with thecurrent_useractor, becausemain.jsruns on every page.
What the earlier steps provide
- !261497 (closed): the static Vue 2 import of
main_app.jsis 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.jsand Vue 2 page code both use stay on one Vue 2 copy. Two of them matter here:tooltips: work item attribute popovers call itsdispose()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
isInfectablerule:- Vue 2 lane:
main.jswithoutmain_app.js, every page entry, and the always-loaded bundles - Vue 3 lane:
main_app.js
- Vue 2 lane:
- 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_completein CE and EE,markdown/schema.js, andvue3compat/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.jsstay on Vue 2. All of them are held back by the existingbehaviors/preview_markdown.jsblocklist entry (glql, content_editor and others), as on rollout pages today. - The Vue 2 runtime stays on every page, because
~/commonsand 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
requireof both copies. It ships both copies to every page and does not work under Vite. - A
main.vue3entry. It needs the standalonemainentry above, or about 2,500 page entries.
Screenshots or screen recordings
Not recorded yet.
How to set up and validate locally
-
Enable the flag in the Rails console:
Feature.enable(:vue3_migrate_main) -
Open a project page, such as
http://127.0.0.1:3000/gitlab-org/gitlab-test. -
In the console, check for
[gitlab] [V] Using Vue.js 3 (with @vue/compat) for PanelWidthResizerRoot. -
Run
document.querySelectorAll('[data-gitlab-vue3-app]'). It returns the panel resizer roots. -
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.
- Press
-
Hover a user link. The user popover opens.
-
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
- Step 3 of 3. Follows !261497 (closed) (cleanup) and !261499 (merged) (blocklist).
- Part of #623999 — Vue 3 migration of global bundles and
main - Rollout issue: #633362
- Split out of !261461 (closed), which holds all three steps in one commit
- Alternative that changes the chunk graph: !252290 (closed)
- Shared tooltips and popovers state: #628699