{"id":28436229,"url":"https://github.com/eurusik/ngx-mf-remote-loader","last_synced_at":"2025-06-26T11:33:24.814Z","repository":{"id":296410843,"uuid":"993330018","full_name":"eurusik/ngx-mf-remote-loader","owner":"eurusik","description":"SSR-compatible remote module loader for Angular Micro Frontends. Seamlessly load remote modules in both browser and server environments with a unified API. Built for Angular 17+ with Nx support.","archived":false,"fork":false,"pushed_at":"2025-06-04T10:49:24.000Z","size":2568,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-06-14T03:03:38.743Z","etag":null,"topics":["module-federation","nx","ssr"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eurusik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-30T15:49:57.000Z","updated_at":"2025-06-04T10:49:25.000Z","dependencies_parsed_at":"2025-05-30T20:26:13.619Z","dependency_job_id":"74e856a2-b38f-4d9e-b74c-66ad6040e2ee","html_url":"https://github.com/eurusik/ngx-mf-remote-loader","commit_stats":null,"previous_names":["eurusik/ngx-mf-remote-loader"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/eurusik/ngx-mf-remote-loader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eurusik%2Fngx-mf-remote-loader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eurusik%2Fngx-mf-remote-loader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eurusik%2Fngx-mf-remote-loader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eurusik%2Fngx-mf-remote-loader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eurusik","download_url":"https://codeload.github.com/eurusik/ngx-mf-remote-loader/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eurusik%2Fngx-mf-remote-loader/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262056420,"owners_count":23251676,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["module-federation","nx","ssr"],"created_at":"2025-06-05T21:42:04.584Z","updated_at":"2025-06-26T11:33:24.790Z","avatar_url":"https://github.com/eurusik.png","language":"TypeScript","funding_links":[],"categories":["Architecture and Advanced Topics"],"sub_categories":["Micro-Frontends"],"readme":"# ngx-mf-remote-loader\n\nSSR-compatible dynamic remote module loader for Angular + Nx Micro Frontends.\n\n## Features\n\n- SSR-compatible remote module loading\n- Works with Angular and Nx Micro Frontends\n- Simple API for loading remote modules\n- Separate implementations for browser and server environments\n\n## Usage\n\n### Basic Setup\n\n1. First, provide the appropriate `RemoteLoader` implementation in your app module:\n\n```typescript\n// app.module.ts\nimport { NgModule } from '@angular/core';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { RemoteLoader, RemoteLoaderBrowser } from 'ngx-mf-remote-loader';\nimport { AppComponent } from './app.component';\n\n@NgModule({\n  declarations: [AppComponent],\n  imports: [BrowserModule],\n  providers: [\n    {\n      provide: RemoteLoader,\n      useClass: RemoteLoaderBrowser\n    }\n  ],\n  bootstrap: [AppComponent]\n})\nexport class AppModule {}\n```\n\n2. Use the `remoteLoader` function in your routes:\n\n```typescript\n// app-routing.module.ts\nimport { NgModule } from '@angular/core';\nimport { RouterModule, Routes } from '@angular/router';\nimport { remoteLoader } from 'ngx-mf-remote-loader';\n\nconst routes: Routes = [\n  {\n    path: 'remote-feature',\n    loadChildren: remoteLoader('remoteAppName')\n  }\n];\n\n@NgModule({\n  imports: [RouterModule.forRoot(routes)],\n  exports: [RouterModule]\n})\nexport class AppRoutingModule {}\n```\n\n### SSR Configuration\n\nFor SSR applications, you need to provide different implementations for browser and server:\n\n```typescript\n// app.module.ts\nimport { NgModule } from '@angular/core';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { RemoteLoader } from 'ngx-mf-remote-loader';\nimport { AppComponent } from './app.component';\nimport { PLATFORM_ID, inject } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\n\n@NgModule({\n  declarations: [AppComponent],\n  imports: [BrowserModule],\n  providers: [\n    {\n      provide: RemoteLoader,\n      useFactory: () =\u003e {\n        const platformId = inject(PLATFORM_ID);\n        if (isPlatformBrowser(platformId)) {\n          const { RemoteLoaderBrowser } = require('ngx-mf-remote-loader');\n          return new RemoteLoaderBrowser();\n        } else {\n          const { RemoteLoaderServer } = require('ngx-mf-remote-loader');\n          return new RemoteLoaderServer();\n        }\n      }\n    }\n  ],\n  bootstrap: [AppComponent]\n})\nexport class AppModule {}\n```\n\n### Customizing Server-Side Loading\n\nFor server-side rendering, you need to customize the `RemoteLoaderServer` implementation to handle your specific remote modules. There are two main approaches:\n\n#### Approach 1: Using registerRemoteModule\n\n```typescript\n//# ngx-mf-remote-loader-server.ts\nimport { RemoteLoaderServer, RemoteItems } from 'ngx-mf-remote-loader';\n\nexport class CustomRemoteLoaderServer extends RemoteLoaderServer {\n  constructor() {\n    super();\n    // Register all your remote modules\n    this.registerRemoteModule('app1', 'Module', () =\u003e import('app1/Module'));\n    this.registerRemoteModule('app2', 'Module', () =\u003e import('app2/Module'));\n    // Add more registrations as needed\n  }\n  \n  // You can also override the load method to add custom logic\n  load(remoteName: string, remoteModule: RemoteItems): Promise\u003cany\u003e {\n    // Custom logic here if needed\n    return super.load(remoteName, remoteModule);\n  }\n}\n```\n\n#### Approach 2: Direct switch case implementation\n\nThis approach directly handles the module loading with switch cases:\n\n```typescript\n//# ngx-mf-remote-loader-server.ts\nimport { RemoteLoader, RemoteItems } from 'ngx-mf-remote-loader';\n\n// Define your specific remote module types for better type safety\ntype RemotesKey =\n  | 'app1'\n  | 'app2'\n  | 'app3-component';\n\nexport class CustomRemoteLoaderServer extends RemoteLoader {\n  load(remoteName: RemotesKey, remoteModule: RemoteItems): Promise\u003cany\u003e {\n    switch (remoteName + remoteModule) {\n      case 'app1Module':\n        // eslint-disable-next-line @nx/enforce-module-boundaries\n        return import('app1/Module').then((m) =\u003e m.RemoteEntryModule);\n      case 'app2Module':\n        // eslint-disable-next-line @nx/enforce-module-boundaries\n        return import('app2/Module').then((m) =\u003e m.RemoteEntryModule);\n      case 'app3-componentCarouselComponent':\n        // eslint-disable-next-line @nx/enforce-module-boundaries\n        return import('app3-component/CarouselComponent');\n      // Add more cases as needed\n      default:\n        throw new Error(`Remote module ${remoteName}/${remoteModule} not found`);\n    }\n  }\n}\n```\n\n## Module Federation Manifest Setup\n\n**Important:** Setting up the module-federation.manifest.json file is a required step for ngx-mf-remote-loader to work properly. This manifest is essential for mapping remote module names to their URLs and is a crucial part of the Nx Module Federation setup that our library depends on.\n\n### Manifest Structure\n\nThe manifest file is a simple JSON mapping of remote module names to their URLs:\n\n```json\n{\n  \"remote-app-1\": \"http://localhost:4201\",\n  \"remote-app-2\": \"http://localhost:4202\",\n  \"remote-app-3\": \"http://localhost:4203\"\n}\n```\n\n### Loading the Manifest\n\n**Required Step:** For ngx-mf-remote-loader to function correctly, you must load the manifest file in your host application and set the remote definitions as shown below:\n\n#### Using setRemoteDefinitions (Deprecated in Nx 22)\n\n```typescript\n// main.ts\nimport { setRemoteDefinitions } from '@nx/angular/mf';\n\n// Load the manifest file at application startup\nfetch('assets/module-federation.manifest.json')\n  .then((res) =\u003e res.json())\n  .then((definitions) =\u003e setRemoteDefinitions(definitions))\n  .then(() =\u003e import('./bootstrap').catch((err) =\u003e console.error(err)));\n```\n\n#### Using init() from @module-federation/enhanced/runtime (Recommended)\n\n```typescript\n// main.ts\nimport { init } from '@module-federation/enhanced/runtime';\n\n// Example of direct initialization\ninit({\n  name: 'host',\n  remotes: [{\n    name: 'my-remote-app',\n    entry: 'http://localhost:4201/mf-manifest.json'\n  }]\n});\n\n// Or loading from a manifest file\nfetch('assets/module-federation.manifest.json')\n  .then((res) =\u003e res.json())\n  .then((definitions) =\u003e {\n    const remotes = Object.entries(definitions).map(([name, entry]) =\u003e ({\n      name,\n      entry\n    }));\n    \n    return init({\n      name: 'host',\n      remotes\n    });\n  })\n  .then(() =\u003e import('./bootstrap').catch((err) =\u003e console.error(err)));\n```\n\n\u003e **Note:** `setRemoteDefinitions` will be removed in Nx 22. It's recommended to migrate to `init()` from `@module-federation/enhanced/runtime`.\n\n#### Workaround for Lazy-Loaded Modules\n\nIf your application has calls to `loadRemoteModule` inside lazy-loaded NgModules, you might encounter issues with loading federated modules. Here's a workaround that makes remote definitions available globally:\n\n```typescript\n// main.ts\nimport { setRemoteDefinitions } from '@nx/angular/mf';\n\nfetch('assets/module-federation.manifest.json')\n  .then((res) =\u003e res.json())\n  .then((remoteDefinitions) =\u003e {\n    setRemoteDefinitions(remoteDefinitions);\n    \n    // Workaround: Make remote definitions available globally\n    // This helps with loading federated modules in lazy-loaded NgModules\n    // See https://github.com/nrwl/nx/issues/27842\n    Object.assign(globalThis, { remoteDefinitions });\n  })\n  .then(() =\u003e import('./bootstrap').catch((err) =\u003e console.error(err)));\n```\n\nThis workaround assigns the remote definitions to the global scope, making them accessible to lazy-loaded modules that need to resolve remote module URLs.\n\nThis initialization step is essential for our library to properly resolve and load remote modules.\n\n### How It Works\n\n1. **Initialization**: The host application loads the manifest file and passes it to `setRemoteDefinitions()`\n2. **Remote Loading**: When `loadRemoteModule()` is called, it:\n   - Looks up the remote's URL in the manifest\n   - Dynamically loads the JavaScript from that URL\n   - Returns the requested module or component\n\n### Environment-Specific Manifests\n\nYou can create environment-specific manifest files for different deployment environments:\n\n```\n/assets/\n  module-federation.manifest.dev.json\n  module-federation.manifest.prod.json\n  module-federation.manifest.json  # The active manifest\n```\n\nDuring the build process, you can copy the appropriate environment-specific manifest to the main module-federation.manifest.json file.\n\n### Advanced: Dynamic Manifest Updates\n\nFor applications that need to update remote URLs at runtime:\n\n```typescript\nimport { setRemoteDefinitions } from '@nx/angular/mf';\n\n// Fetch an updated manifest from your API\nfetchUpdatedManifest().then(newDefinitions =\u003e {\n  setRemoteDefinitions(newDefinitions);\n  // Now loadRemoteModule will use the updated URLs\n});\n```\n\n### Troubleshooting\n\nCommon issues with module federation manifest:\n\n- **404 Not Found**: Ensure the manifest file is correctly copied to the assets folder during build\n- **Remote Loading Failures**: Verify that the URLs in your manifest are correct and accessible\n- **SSR Errors**: For server-side rendering, ensure you've properly configured the server-side loader\n\n### Implementation Details\n\nUnder the hood, the library uses Nx's `loadRemoteModule` function, which relies on the manifest definitions:\n\n```typescript\n// RemoteLoaderBrowser implementation\nexport class RemoteLoaderBrowser extends RemoteLoader {\n  load(remoteName: string, remoteModule: RemoteItems): Promise\u003cany\u003e {\n    return loadRemoteModule(remoteName, './' + remoteModule).then((m) =\u003e {\n      return remoteModule === 'Module' ? m.RemoteEntryModule : m;\n    });\n  }\n}\n```\n\nThis approach ensures compatibility with the Nx Module Federation system while providing a more convenient API for Angular applications.\n\n## Recommended Companion Tool: nx-mf-remote-loader-generator\n\nFor the best development experience, we strongly recommend using our companion tool [nx-mf-remote-loader-generator](https://github.com/eurusik/nx-mf-remote-loader-generator) which automates the creation and configuration of remote components that work seamlessly with this library.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feurusik%2Fngx-mf-remote-loader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feurusik%2Fngx-mf-remote-loader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feurusik%2Fngx-mf-remote-loader/lists"}