Skip to content

feat(lightspeed): add dedicated FAB module export for new frontend system#3397

Open
its-mitesh-kumar wants to merge 2 commits into
redhat-developer:mainfrom
its-mitesh-kumar:feat/lightspeed-fab-module-export
Open

feat(lightspeed): add dedicated FAB module export for new frontend system#3397
its-mitesh-kumar wants to merge 2 commits into
redhat-developer:mainfrom
its-mitesh-kumar:feat/lightspeed-fab-module-export

Conversation

@its-mitesh-kumar

@its-mitesh-kumar its-mitesh-kumar commented Jun 13, 2026

Copy link
Copy Markdown
Member

Description

Adds a dedicated module export (./lightspeed-fab-module) for the Lightspeed Floating Action Button (FAB) component. This enables the FAB to be registered as a standalone app-root-wrapper extension in the new frontend system without importing the entire alpha entrypoint. The change includes the new export file, package.json export/typesVersions entries, and the dynamic plugin exposed module configuration.

Screen recording

S_.2026-06-14.at.5.11.52.AM.mov

Problem

In the New Frontend System (NFS), the Lightspeed FAB was not appearing because of three layered gaps:

Gap 1: NFS Only Loads Default Exports

┌─────────────────────────────────────────────────────────────────┐
│            RHDH NFS Dynamic Plugin Loader                       │
│                                                                 │
│   For each remote plugin:                                       │
│     1. Read /remotes endpoint → get exposed modules list        │
│     2. For each exposed module → load its DEFAULT export        │
│     3. Register the default export as a frontend feature        │
│                                                                 │
│   ⚠️  Named exports are IGNORED                                 │
└─────────────────────────────────────────────────────────────────┘

Gap 2: AppRootWrapperBlueprint Can't Live in createFrontendPlugin

┌─────────────────────────────────────────────────────────────────┐
│  Backstage NFS Extension Registration Rules                     │
│                                                                 │
│  ✅ createFrontendPlugin({ pluginId: 'lightspeed' })            │
│     → Can contain: PageBlueprint, ApiBlueprint, etc.            │
│                                                                 │
│  ❌ createFrontendPlugin({ pluginId: 'lightspeed' })            │
│     → CANNOT contain: AppRootWrapperBlueprint                   │
│       (Extension gets registered but never recognized           │
│        by the app shell → "does not exist" error)               │
│                                                                 │
│  ✅ createFrontendModule({ pluginId: 'app' })                   │
│     → CAN contain: AppRootWrapperBlueprint                      │
│       (Module extends the 'app' plugin directly)                │
└─────────────────────────────────────────────────────────────────┘

Gap 3: The lightspeedFABModule Was a Named Export Only

src/alpha/index.tsx (BEFORE fix):
┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│  export const lightspeedFABModule = createFrontendModule(...)   │
│         ^^^^^^                                                  │
│         Named export only! NFS loader NEVER sees this.          │
│                                                                 │
│  export default createFrontendPlugin({                          │
│    extensions: [api, page, drawer]  ← No FAB here either       │
│  });                                                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Solution

Expose the FAB module as a separate Module Federation entry point with its own default export.

┌─────────────────────────────────────────────────────────────────────────┐
│                                                                         │
│  BEFORE (broken)                        AFTER (working)                 │
│  ════════════════                       ═══════════════                  │
│                                                                         │
│  Module Federation Remote               Module Federation Remote        │
│  ┌───────────────────────┐              ┌───────────────────────┐       │
│  │ Exposed Modules:      │              │ Exposed Modules:      │       │
│  │                       │              │                       │       │
│  │  "." → src/index.ts   │              │  "." → src/index.ts   │       │
│  │    (legacy)           │              │    (legacy)           │       │
│  │                       │              │                       │       │
│  │  "./alpha"            │              │  "./alpha"            │       │
│  │    → default export:  │              │    → default export:  │       │
│  │      FrontendPlugin   │              │      FrontendPlugin   │       │
│  │      (page, api,      │              │      (page, api,      │       │
│  │       drawer)         │              │       drawer)         │       │
│  │                       │              │                       │       │
│  │  ❌ FAB module is     │              │  "./lightspeed-fab-   │  ← NEW│
│  │     trapped as a      │              │    module"            │       │
│  │     named export      │              │    → default export:  │       │
│  │     inside alpha,     │              │      FrontendModule   │       │
│  │     never loaded      │              │      (FAB + Provider) │       │
│  │                       │              │                       │       │
│  └───────────────────────┘              └───────────────────────┘       │
│                                                                         │
│  NFS Loader loads:                      NFS Loader loads:               │
│    ✅ alpha → Plugin (page works)         ✅ alpha → Plugin             │
│    ❌ FAB never discovered                ✅ lightspeed-fab-module       │
│                                              → FAB + Provider           │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Changes Made

┌─ 1. New file: src/alpha/lightspeedFABModuleExport.ts ────────────┐
│                                                                   │
│   export { lightspeedFABModule as default } from './index';       │
│                                                                   │
│   ↑ Re-exports the named export as a DEFAULT export               │
│     so the NFS loader can discover it                             │
└───────────────────────────────────────────────────────────────────┘

┌─ 2. package.json ────────────────────────────────────────────────┐
│                                                                   │
│   "exports": {                                                    │
│     "./lightspeed-fab-module": "./src/alpha/...Export.ts"  ← NEW  │
│   }                                                               │
│   "scalprum": {                                                   │
│     "exposedModules": {                                           │
│       "LightspeedFABModule": "./src/alpha/...Export.ts"    ← NEW  │
│     }                                                             │
│   }                                                               │
│                                                                   │
│   ↑ Tells webpack MF to bundle it as a separate entry point      │
│     AND tells RHDH backend to advertise it in /remotes            │
└───────────────────────────────────────────────────────────────────┘

┌─ 3. app-config: correct extension namespace ─────────────────────┐
│                                                                   │
│   app.extensions:                                                 │
│     - app-root-wrapper:app/lightspeed-fab    ← namespace = "app"  │
│                                                NOT "lightspeed"   │
│                                                                   │
│   ↑ Because the module uses pluginId: 'app', its extensions       │
│     get the 'app' namespace, not 'lightspeed'                     │
└───────────────────────────────────────────────────────────────────┘

Why This Is the Best Approach

Alternative Considered Why It Failed
Put FAB inside createFrontendPlugin() AppRootWrapperBlueprint not recognized from a plugin (only from module with app scope)
Export array [plugin, module] as default from alpha/index.tsx MF build drops the module entirely when default isn't a single object
✅ Separate MF entry point with its own default export Follows NFS rules, proper app scope, auto-discovered, zero coupling

In one sentence: The NFS loader only loads default exports of separately-exposed Module Federation modules, and AppRootWrapperBlueprint must come from a module targeting pluginId: 'app' — so we gave the FAB module its own entry point with its own default export.

Testing in RHDH-local

Prerequisites

  • rhdh-local repo cloned
  • Podman installed and running
  • NFS mode enabled in .env:
    APP_CONFIG_app_packageName=app-next
    ENABLE_STANDARD_MODULE_FEDERATION=true
    

Steps

  1. Build the plugin export:
cd /path/to/rhdh-plugins/workspaces/lightspeed/plugins/lightspeed
sudo npx --yes @janus-idp/cli package export-dynamic-plugin \
  --dynamic-plugins-root /<path-to>/rhdh-local/dynamic-plugins-root \
  --dev
  1. Configure dynamic-plugins to use local plugin:
    In developer-lightspeed/configs/dynamic-plugins/dynamic-plugins.lightspeed.yaml:

    plugins:
      - package: './local-plugins/red-hat-developer-hub-backstage-plugin-lightspeed'
        disabled: false
  2. Enable the FAB extension in app-config:
    In developer-lightspeed/configs/app-config/app-config.lightspeed.local.yaml:

    app:
      extensions:
        - app-root-wrapper:app/lightspeed-fab
  3. Start RHDH-local with dynamic-plugins-root override:

    podman compose -f compose.yaml \
      -f developer-lightspeed/compose.yaml \
      -f compose-dynamic-plugins-root.yaml \
      up -d
  4. Verify:

    • Open http://localhost:7007
    • Confirm the Lightspeed FAB (floating action button) appears in the bottom-right corner
    • Navigate to /lightspeed and confirm the page loads
    • Check /remotes endpoint includes lightspeed-fab-module:
      curl -s http://localhost:7007/.backstage/dynamic-features/remotes | python3 -m json.tool
      Expected: "exposedModules": [".", "alpha", "lightspeed-fab-module"]

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

@rhdh-gh-app

rhdh-gh-app Bot commented Jun 13, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Changed Packages

Package Name Package Path Changeset Bump Current Version
backend workspaces/lightspeed/packages/backend none v0.0.59
@red-hat-developer-hub/backstage-plugin-lightspeed workspaces/lightspeed/plugins/lightspeed minor v2.9.1

@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 54.01%. Comparing base (172d39f) to head (173de65).
⚠️ Report is 7 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3397      +/-   ##
==========================================
- Coverage   54.01%   54.01%   -0.01%     
==========================================
  Files        2409     2409              
  Lines       87709    87704       -5     
  Branches    24284    24280       -4     
==========================================
- Hits        47377    47372       -5     
  Misses      38757    38757              
  Partials     1575     1575              
Flag Coverage Δ *Carryforward flag
adoption-insights 83.58% <ø> (ø) Carriedforward from 172d39f
ai-integrations 70.03% <ø> (ø) Carriedforward from 172d39f
app-defaults 69.60% <ø> (ø) Carriedforward from 172d39f
augment 46.39% <ø> (ø) Carriedforward from 172d39f
bulk-import 72.69% <ø> (ø) Carriedforward from 172d39f
cost-management 17.48% <ø> (ø) Carriedforward from 172d39f
dcm 60.27% <ø> (ø) Carriedforward from 172d39f
extensions 62.17% <ø> (ø) Carriedforward from 172d39f
global-floating-action-button 74.30% <ø> (ø) Carriedforward from 172d39f
global-header 61.63% <ø> (ø) Carriedforward from 172d39f
homepage 52.60% <ø> (ø) Carriedforward from 172d39f
install-dynamic-plugins 56.23% <ø> (ø) Carriedforward from 172d39f
konflux 91.01% <ø> (ø) Carriedforward from 172d39f
lightspeed 68.46% <ø> (-0.03%) ⬇️
mcp-integrations 85.46% <ø> (ø) Carriedforward from 172d39f
orchestrator 37.33% <ø> (ø) Carriedforward from 172d39f
quickstart 62.09% <ø> (ø) Carriedforward from 172d39f
sandbox 79.56% <ø> (ø) Carriedforward from 172d39f
scorecard 83.93% <ø> (ø) Carriedforward from 172d39f
theme 64.54% <ø> (ø) Carriedforward from 172d39f
translations 8.49% <ø> (ø) Carriedforward from 172d39f
x2a 78.79% <ø> (ø) Carriedforward from 172d39f

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 172d39f...173de65. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

its-mitesh-kumar and others added 2 commits June 14, 2026 06:04
…stem

Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: its-mitesh-kumar <itsmiteshkumar98@gmail.com>
@its-mitesh-kumar its-mitesh-kumar force-pushed the feat/lightspeed-fab-module-export branch from ec7cd10 to 173de65 Compare June 14, 2026 00:46
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant