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:

  1. The eslint rule + codemod: local-rules/vue3-init-vue-app (registered, not enforced) and scripts/frontend/codemods/vue3_init_vue_app.mjs to batch-fix and list residue.
  2. The migration: 465 files in the canonical shape. Bootstrap specs mock initVueApp and assert wiring instead of mocking the vue module.
  3. Review fixes (route reactivity in the web IDE link bootstrap; dropped stray noFlip provide).
  4. 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's if (!el) guard is load-bearing — the old code evaluated props lazily inside render() (never called when the mount element is absent), while initVueApp evaluates the props option eagerly, so without the guard a missing #js-template-selectors-menu would throw on el.dataset.
  • projects/project_new.js's no-op guard is load-bearing for panels without name/path fields (Import, CI/CD for external repository), which bindEvents() visits unconditionally when LegacyContainer adopts them.
  • invite_members/init_invite_members_modal.js keeps its module-scope Vue.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 --list reports 0 fixable sites.
  • Pre-split content was proven on the full run-all-rspec + run-rspec-vue3 matrix; a fresh labeled pipeline runs on each re-cut.
Edited by Illya Klymov

Merge request reports

Loading
Loading