Convert new Vue app-root bootstraps to initVueApp
What does this MR do and why?
Migrates new Vue({ el, render }) app-root bootstraps to initVueApp (added in !247298 (merged)), keeping this MR to the single mechanical codemod shape — every hunk is the same { el, component, props, events } conversion, so the diff reviews as one pattern. Module-scope plugin installs (Vue.use(...)) are left untouched everywhere; folding them into per-app plugins is deliberately out of scope.
Commits:
- The eslint rule + codemod:
local-rules/vue3-init-vue-app(registered, not enforced) andscripts/frontend/codemods/vue3_init_vue_app.mjsto batch-fix and list residue. - The migration: 465 files in the canonical shape. Bootstrap specs mock
initVueAppand assert wiring instead of mocking thevuemodule. - Review fixes (route reactivity in the web IDE link bootstrap; dropped stray
noFlipprovide). - Deferrals (three review rounds): everything that needs something unusual — or that requires a non-frontend-maintainer approval per CODEOWNERS — is restored to its master state here and re-applied in stacked MRs targeting this branch:
| Stacked MR | Class | Files |
|---|---|---|
| !248045 | Hand-written render functions (normalizeRender + compatH, incl. version-branched renders) |
66 + 2 specs |
| !248046 | SPA router roots (SpaRouterView helper) |
6 + helper + spec |
| !248047 | google_cloud attrs-as-props (needs component prep first) | 6 |
| !248082 | Detached mounts, teardown choreography, options factories | 10 |
| !248301 (closed) | [Authentication] CODEOWNERS section (access_tokens, authentication/*, oauth_application, ee/ldap, ee/password) |
11 |
| !248302 (closed) | [Container registry] CODEOWNERS section (container_registry/explorer, harbor_registry) |
2 |
On Vue 2 (production) initVueApp is exactly the replaced call — this is a behavior-preserving refactor. The deferred new Vue sites (plus the 11 sites blocked on the defineAsyncComponent wave) lint clean because the rule stays unenforced; the infection scanner's detectAppRoot recognizes both bootstrap shapes.
Review questions answered in place:
blob/filepath_form'sif (!el)guard is load-bearing — the old code evaluated props lazily insiderender()(never called when the mount element is absent), whileinitVueAppevaluates thepropsoption eagerly, so without the guard a missing#js-template-selectors-menuwould throw onel.dataset.projects/project_new.js's no-op guard is load-bearing for panels without name/path fields (Import, CI/CD for external repository), whichbindEvents()visits unconditionally whenLegacyContaineradopts them.invite_members/init_invite_members_modal.jskeeps its module-scopeVue.use(GlToast)untouched — consistent with the plugin-install policy above.
Stacked on !247298 (merged) (machinery, merged) → targets master.
Verification
- All mapped bootstrap specs green on the default lane and on
VUE_VERSION=3. - Rule unit tests: 191 passing under vitest;
codemod --listreports 0 fixable sites. - Pre-split content was proven on the full
run-all-rspec+run-rspec-vue3matrix; a fresh labeled pipeline runs on each re-cut.