Add script to trace removable Vue provide values

What does this MR do?

Part of #604216

Adds scripts/frontend/trace_provide_inject_usage.mjs, a developer aid that reports whether the values a file provide:s can be removed — the inverse of the local-rules/vue-no-unused-injects rule (!242236 (merged)), which catches dead inject declarations. Removing a dead provide can't be decided by a single file, so this script reasons across the component tree.

How it decides

For each top-level provide key:

  1. Is it injected anywhere? AST-scan of all inject declarations (handling inject: { local: { from: 'x' } } aliasing and Composition-API inject('x')). A key injected nowhere is unconditionally REMOVABLE (sound — nothing can consume it).
  2. Is an injector reachable? Otherwise it walks the module import graph from the provider with dependency-cruiser (reusing the webpack aliases, following dynamic import()). Because a component can only render as a descendant if its module is importable from the root, import-graph unreachability is a sound proof of "not a descendant" → LIKELY REMOVABLE. A reachable injector is flagged IN USE (possible) for runtime confirmation (shared barrels can link unrelated modules, so reachability is possible-not-proven).

Dynamic boundaries that limit soundness — global Vue.component(...) registration and unresolvable (computed) dynamic imports — are surfaced as INCONCLUSIVE.

Usage

node scripts/frontend/trace_provide_inject_usage.mjs <file|glob> [<file|glob>…]
  • Accepts files or globs (quote globs to avoid shell expansion); non-.vue/.js matches and unparseable files are skipped.
  • Requires ripgrep (rg) on PATH.
  • Files with a provide: get one line per key, sorted dead-first (⚠️) ahead of in-use (); files without one collapse to a single skipped line.
  • Ends with a cross-file roundup of removal candidates.

Example Output

I picked a pretty extreme example: app/assets/javascripts/boards/index.js:

────────────────────────────────────────────────────────────────────────────────
app/assets/javascripts/boards/index.js — 57 keys
[V] Using Vue.js 2 (compiler 2)
IncrementalWebpackCompiler: Status – disabled
  · 1174 modules reached

  ⚠️  REMOVABLE: Injected nowhere (3)
    boardWeight
    emailsEnabled
    multipleAssigneesFeatureAvailable

  ⚠️  LIKELY REMOVABLE: No injector reachable (6)
    labelsFilterBasePath            ← ~/vue_shared/issuable/create/components/issuable_label_selector.vue
    allowLabelEdit                  ← ~/sidebar/components/labels/labels_select_widget/labels_select_root.vue
    hasIterationsFeature            ← ee/work_items/pages/planning_view.vue +4
    hasIssueWeightsFeature          ← ee/work_items/pages/planning_view.vue +5
    hasIssuableHealthStatusFeature  ← ee/work_items/pages/planning_view.vue +6
    hasOkrsFeature                  ← ~/work_items/pages/planning_view.vue +1

  ✅ IN USE: Injector reachable (48)
    initialBoardId                  ← ~/boards/components/board_app.vue
    disabled                        ← ee/boards/components/epics_swimlanes.vue +5 reachable
    groupId                         ← ee/boards/components/group_select.vue +2 reachable
    rootPath                        ← ~/boards/components/board_card_inner.vue
    fullPath                        ← ee/boards/components/group_select.vue +26 reachable
    groupPath                       ← ~/work_items/components/create_work_item.vue +2 reachable
    initialFilterParams             ← ~/boards/components/board_filtered_search.vue +1 reachable
    boardBaseUrl                    ← ee/boards/components/board_filtered_search.vue +2 reachable
    boardType                       ← ee/boards/components/issues_lane_list.vue +14 reachable
    isGroup                         ← ~/work_items/components/work_item_detail_panel.vue +2 reachable
    isGroupBoard                    ← ee/boards/components/issue_board_filtered_search.vue +9 reachable
    isProjectBoard                  ← ee/boards/components/milestone_select.vue +4 reachable
    currentUserId                   ← ~/boards/components/board_list_header.vue
    labelsManagePath                ← ~/sidebar/components/labels/labels_select_widget/dropdown_footer.vue +1 reachable
    releasesFetchPath               ← ~/boards/components/issue_board_filtered_search.vue
    timeTrackingLimitToHours        ← ~/boards/components/issue_time_estimate.vue +1 reachable
    issuableType                    ← ee/boards/components/board_add_new_column.vue +11 reachable
    hasMissingBoards                ← ~/boards/components/boards_selector.vue
    weights                         ← ~/boards/components/boards_selector.vue
    isIssueBoard                    ← ee/boards/components/board_form.vue +7 reachable
    isEpicBoard                     ← ee/boards/components/board_form.vue +10 reachable
    reportAbusePath                 ← ~/work_items/components/work_item_abuse_modal.vue
    issuesListPath                  ← ee/work_items/components/work_item_custom_fields_single_select.vue +2 reachable
    canUpdate                       ← ~/sidebar/components/sidebar_editable_item.vue
    canAdminList                    ← ~/boards/components/board_settings_sidebar.vue +2 reachable
    canAdminBoard                   ← ~/boards/components/boards_selector.vue
    allowLabelCreate                ← ~/sidebar/components/labels/labels_select_widget/dropdown_footer.vue
    isSignedIn                      ← ~/boards/components/issue_board_filtered_search.vue +1 reachable
    canAdminLabel                   ← ~/work_items/components/work_item_labels.vue
    epicFeatureAvailable            ← ee/boards/components/issue_board_filtered_search.vue
    iterationFeatureAvailable       ← ee/boards/components/issue_board_filtered_search.vue
    weightFeatureAvailable          ← ee/boards/components/board_list_header.vue +1 reachable
    scopedLabelsAvailable           ← ee/boards/components/board_add_new_column.vue +3 reachable
    milestoneListsAvailable         ← ee/boards/components/board_add_new_column.vue
    assigneeListsAvailable          ← ee/boards/components/board_add_new_column.vue
    iterationListsAvailable         ← ee/boards/components/board_add_new_column.vue
    healthStatusFeatureAvailable    ← ee/boards/components/issue_board_filtered_search.vue
    allowScopedLabels               ← ~/sidebar/components/labels/labels_select_widget/dropdown_value.vue
    swimlanesFeatureAvailable       ← ~/boards/components/board_top_bar.vue
    multipleIssueBoardsAvailable    ← ~/boards/components/boards_selector.vue
    scopedIssueBoardFeatureEnabled  ← ~/boards/components/boards_selector.vue
    allowSubEpics                   ← ~/boards/components/board_card_inner.vue
    hasScopedLabelsFeature          ← ~/vue_shared/components/filtered_search_bar/tokens/label_token.vue
    hasSubepicsFeature              ← ~/work_items/components/work_item_change_type_modal.vue +3 reachable
    hasLinkedItemsEpicsFeature      ← ~/work_items/components/work_item_detail.vue
    hasCustomFieldsFeature          ← ee/boards/components/issue_board_filtered_search.vue +2 reachable
    statusListsAvailable            ← ee/boards/components/issue_board_filtered_search.vue +1 reachable
    hasStatusFeature                ← ee/boards/components/issue_board_filtered_search.vue

================================================================================
Removal candidates (9):
app/assets/javascripts/boards/index.js:
  REMOVABLE        boardWeight
  REMOVABLE        emailsEnabled
  REMOVABLE        multipleAssigneesFeatureAvailable
  LIKELY-REMOVABLE labelsFilterBasePath
  LIKELY-REMOVABLE allowLabelEdit
  LIKELY-REMOVABLE hasIterationsFeature
  LIKELY-REMOVABLE hasIssueWeightsFeature
  LIKELY-REMOVABLE hasIssuableHealthStatusFeature
  LIKELY-REMOVABLE hasOkrsFeature

IN USE = possible descent (confirm at runtime); it is not proof of use.

This report, in combination with clean up for .eslint_todo/local-rules-vue-no-unused-injects.mjs could help remove a lot of unused code.

Documentation

This MR also documents the workflow so both humans and agents can find it:

  • doc/development/fe_guide/tooling.md — a Trace unused Vue provides section covering how to run the script, what each verdict means, and how to remove a key layer by layer.
  • doc/development/fe_guide/vue.md — the provide/inject section points to the tooling docs.

Notes for reviewers

  • Developer tooling only, I don't think this could be wired to any CI verification. It's an investigation aid (the IN USE verdict is intentionally "possible", to be confirmed at runtime!).
Edited by Miguel Rincon

Merge request reports

Loading