Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native federation angular and firebase #650

Open
raliand opened this issue Sep 23, 2024 · 5 comments
Open

Native federation angular and firebase #650

raliand opened this issue Sep 23, 2024 · 5 comments

Comments

@raliand
Copy link

raliand commented Sep 23, 2024

I am trying to create an angular project to work with the Firebase (@angular/fire) and Native Federation in an NX Monorepo.

This is my setup:
tsconfig.base.json

{
  "compileOnSave": false,
  "compilerOptions": {
    "rootDir": ".",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "ES2022",
    "module": "esnext",
    "lib": ["ES2022", "dom"],
    "skipLibCheck": true,
    "skipDefaultLibCheck": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "baseUrl": ".",
    "paths": {
      "@ethi/common": ["libs/common/src/index.ts"],
      "@ethi/schema": ["libs/schema/src/index.ts"]
    }
  },
  "exclude": ["node_modules", "tmp"]
}

package.json

{
  "name": "@ethi/source",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {},
  "private": true,
  "dependencies": {
    "@angular/animations": "~18.2.0",
    "@angular/common": "~18.2.0",
    "@angular/compiler": "~18.2.0",
    "@angular/core": "~18.2.0",
    "@angular/fire": "^18",
    "@angular/forms": "~18.2.0",
    "@angular/platform-browser": "~18.2.0",
    "@angular/platform-browser-dynamic": "~18.2.0",
    "@angular/router": "~18.2.0",
    "es-module-shims": "^1.5.12",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.14.3"
  },
  "devDependencies": {
    "@angular-architects/native-federation": "^18.2.2",
    "@angular-devkit/build-angular": "~18.2.0",
    "@angular-devkit/core": "~18.2.0",
    "@angular-devkit/schematics": "~18.2.0",
    "@angular-eslint/eslint-plugin": "^18.0.1",
    "@angular-eslint/eslint-plugin-template": "^18.0.1",
    "@angular-eslint/template-parser": "^18.0.1",
    "@angular/cli": "~18.2.0",
    "@angular/compiler-cli": "~18.2.0",
    "@angular/language-service": "~18.2.0",
    "@nx/angular": "19.7.3",
    "@nx/eslint": "19.7.3",
    "@nx/eslint-plugin": "19.7.3",
    "@nx/jest": "19.7.3",
    "@nx/js": "19.7.3",
    "@nx/node": "19.7.3",
    "@nx/web": "19.7.3",
    "@nx/workspace": "19.7.3",
    "@schematics/angular": "~18.2.0",
    "@swc-node/register": "~1.9.1",
    "@swc/core": "~1.5.7",
    "@swc/helpers": "~0.5.11",
    "@types/jest": "^29.5.12",
    "@types/node": "18.16.9",
    "@typescript-eslint/eslint-plugin": "^7.16.0",
    "@typescript-eslint/parser": "^7.16.0",
    "@typescript-eslint/utils": "^7.16.0",
    "eslint": "~8.57.0",
    "eslint-config-prettier": "^9.0.0",
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",
    "jest-environment-node": "^29.7.0",
    "jest-preset-angular": "~14.1.0",
    "nx": "19.7.3",
    "prettier": "^2.6.2",
    "ts-jest": "^29.1.0",
    "ts-node": "10.9.1",
    "typescript": "~5.5.2"
  }
}

app.config.ts

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(appRoutes), 
    provideFirebaseApp(() => initializeApp(environments.firebase), 
    provideAuth(() => getAuth()), 
    provideFirestore(() => getFirestore()), 
    provideFunctions(() => getFunctions()),
  ],
};

app.routes.ts

const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['login']);
const redirectLoggedInToHome = () => redirectLoggedInTo(['home']);

export const appRoutes: Routes = [
    {
        path: '',
        component: HomeComponent,
        pathMatch: 'full',
        canActivate: [AuthGuard],
        data: { authGuardPipe: redirectUnauthorizedToLogin },
    },
    {
        path: 'home',
        component: HomeComponent,
        pathMatch: 'full',
        canActivate: [AuthGuard],
        data: { authGuardPipe: redirectUnauthorizedToLogin },
    },
    {
        path: 'login',
        component: LoginPageComponent,
        canActivate: [AuthGuard],
        data: { authGuardPipe: redirectLoggedInToHome },
    },
];

app.component.ts

@Component({
  standalone: true,
  imports: [CommonModule, RouterModule],
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrl: './app.component.css',
})
export class AppComponent {
  title = 'broker-bi';
}

project.json

{
  "name": "broker-bi",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "projectType": "application",
  "prefix": "app",
  "sourceRoot": "apps/broker-bi/src",
  "tags": [],
  "targets": {
    "build": {
      "executor": "@angular-architects/native-federation:build",
      "options": {},
      "configurations": {
        "production": {
          "target": "broker-bi:esbuild:production"
        },
        "development": {
          "target": "broker-bi:esbuild:development",
          "dev": true
        }
      },
      "defaultConfiguration": "production"
    },
    "serve": {
      "executor": "@angular-architects/native-federation:build",
      "options": {
        "target": "broker-bi:serve-original:development",
        "rebuildDelay": 0,
        "dev": true,
        "port": 0
      }
    },
    "extract-i18n": {
      "executor": "@angular-devkit/build-angular:extract-i18n",
      "options": {
        "buildTarget": "broker-bi:build"
      }
    },
    "lint": {
      "executor": "@nx/eslint:lint"
    },
    "test": {
      "executor": "@nx/jest:jest",
      "outputs": [
        "{workspaceRoot}/coverage/{projectRoot}"
      ],
      "options": {
        "jestConfig": "apps/broker-bi/jest.config.ts"
      }
    },
    "serve-static": {
      "executor": "@nx/web:file-server",
      "options": {
        "buildTarget": "broker-bi:build",
        "port": 4200,
        "staticFilePath": "dist/apps/broker-bi/browser",
        "spa": true
      }
    },
    "esbuild": {
      "executor": "@angular-devkit/build-angular:application",
      "outputs": [
        "{options.outputPath}"
      ],
      "options": {
        "outputPath": "dist/apps/broker-bi",
        "index": "apps/broker-bi/src/index.html",
        "browser": "apps/broker-bi/src/main.ts",
        "polyfills": [
          "zone.js",
          "es-module-shims"
        ],
        "tsConfig": "apps/broker-bi/tsconfig.app.json",
        "assets": [
          {
            "glob": "**/*",
            "input": "apps/broker-bi/public"
          }
        ],
        "styles": [
          "apps/broker-bi/src/styles.css"
        ],
        "scripts": []
      },
      "configurations": {
        "production": {
          "budgets": [
            {
              "type": "initial",
              "maximumWarning": "500kb",
              "maximumError": "1mb"
            },
            {
              "type": "anyComponentStyle",
              "maximumWarning": "2kb",
              "maximumError": "4kb"
            }
          ],
          "outputHashing": "all"
        },
        "development": {
          "optimization": false,
          "extractLicenses": false,
          "sourceMap": true
        }
      },
      "defaultConfiguration": "production"
    },
    "serve-original": {
      "executor": "@angular-devkit/build-angular:dev-server",
      "configurations": {
        "production": {
          "buildTarget": "broker-bi:esbuild:production"
        },
        "development": {
          "buildTarget": "broker-bi:esbuild:development"
        }
      },
      "defaultConfiguration": "development",
      "options": {
        "port": 1100
      }
    }
  }
}

When I run the application through nx run broker-bi:serve-original it runs as expected but with no native federation. When I run it through nx run broker-bi:serve I get the following error:

FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - call initializeApp() first (app/no-app).
    at getApp (_angular_fire_auth.r5LpLF_iJr-dev.js:1538:25)
    at getAuth (_angular_fire_auth.r5LpLF_iJr-dev.js:9174:24)
    at _angular_fire.hX3GNn6leI-dev.js:1316:44
    at _angular_fire.hX3GNn6leI-dev.js:1263:57
    at _ZoneDelegate.invoke (zone.js:369:28)
    at _ZoneImpl.run (zone.js:111:43)
    at _NgZone.runOutsideAngular (_angular_core.lGU3keLAQ5-dev.js:4134:24)
    at runOutsideAngular (_angular_fire.hX3GNn6leI-dev.js:1263:33)
    at _angular_fire.hX3GNn6leI-dev.js:1316:17
    at app.config.ts:24:23

What am I dong wrong?

@vladimirs-puzanovs-vr
Copy link

@raliand

I've run into this issue recently and I solved it by adding firebase/app and firebase/messaging to the skip array in federation.config.js like this:

const { withNativeFederation, shareAll } = require('@angular-architects/native-federation/config');

module.exports = withNativeFederation({

  shared: {
    ...shareAll({ singleton: true, strictVersion: false, requiredVersion: 'auto' }),
  },

  skip: [
    'rxjs/ajax',
    'rxjs/fetch',
    'rxjs/testing',
    'rxjs/webSocket',
    'firebase/app',
    'firebase/messaging',
    // Add further packages you don't need at runtime
  ]
});

You will still be able to import and use firebase functions where necessary in your project, e.g.:

import { initializeApp } from 'firebase/app';
import { getMessaging, getToken, onMessage } from 'firebase/messaging';

The reason for this issue occuring might be that firebase is being loaded twice - once when federation plugin sets up all the dependencies via withNativeFederation call and then again when you actually import it somewhere. Same error occurs when using @angular/fire, by the way. @manfredsteyer worth looking into, perhaps?

@raliand
Copy link
Author

raliand commented Oct 25, 2024

@vladimirs-puzanovs-vr Thank you for the suggestion. The point though, is to initialize the firebase app once when bootstrapping the skeleton app and use it across all the federated apps. We do this with module federation and Angular 14 and it works really well. I am trying to understand if it will be possible to do this with an app where we use only standalone components.

@ericaugusto-git
Copy link

Same problem here and unfortunately @vladimirs-puzanovs-vr workaround dind't work for me

My setup:

app.config.ts

export const appConfig: ApplicationConfig = {
  providers: [
  provideFirebaseApp(() => initializeApp({...})),
    provideAuth(() => getAuth()),
]
}

federation.config.js

const { withNativeFederation, shareAll } = require('@angular-architects/native-federation/config');

module.exports = withNativeFederation({

  shared: {
    ...shareAll({ singleton: true, strictVersion: false, requiredVersion: 'auto' }),
  },

  skip: [
    'rxjs/ajax',
    'rxjs/fetch',
    'rxjs/testing',
    'rxjs/webSocket',
    'firebase/app',
    'firebase/messaging',
    'juice',
    'fs'
    // Add further packages you don't need at runtime
  ]
});

angular.json

{
	"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
	"version": 1,
	"newProjectRoot": "projects",
	"projects": {
		"template-vazio": {
			"projectType": "application",
			"schematics": {
				"@schematics/angular:component": {
					"style": "scss"
				}
			},
			"root": "",
			"sourceRoot": "src",
			"prefix": "app",
			"architect": {
				"build": {
					"builder": "@angular-architects/native-federation:build",
					"options": {},
					"configurations": {
						"production": {
							"target": "template-vazio:esbuild:production"
						},
						"development": {
							"target": "template-vazio:esbuild:development",
							"dev": true
						}
					},
					"defaultConfiguration": "production"
				},
				"serve": {
					"builder": "@angular-architects/native-federation:build",
					"options": {
						"target": "template-vazio:serve-original:development",
						"rebuildDelay": 0,
						"dev": true,
						"port": 0
					}
				},
				"extract-i18n": {
					"builder": "@angular-devkit/build-angular:extract-i18n",
					"options": {
						"buildTarget": "template-vazio:build"
					}
				},
				"test": {
					"builder": "@angular-devkit/build-angular:karma",
					"options": {
						"polyfills": [
							"zone.js",
							"zone.js/testing"
						],
						"tsConfig": "tsconfig.spec.json",
						"inlineStyleLanguage": "scss",
						"assets": [
							"src/favicon.ico",
							"src/assets"
						],
						"styles": [
							"src/styles.scss"
						],
						"scripts": []
					}
				},
				"esbuild": {
					"builder": "@angular-devkit/build-angular:application",
					"options": {
						"outputPath": "dist/template-vazio",
						"index": "src/index.html",
						"browser": "src/main.ts",
						"polyfills": [
							"zone.js",
							"es-module-shims"
						],
						"tsConfig": "tsconfig.app.json",
						"inlineStyleLanguage": "scss",
						"assets": [
							"src/favicon.ico",
							"src/assets"
						],
						"styles": [
              "node_modules/@xtr/utilities/src/styles/xtr-styles.scss",
							"node_modules/@nebular/theme/styles/prebuilt/default.css",
							"src/styles.scss"
						],
						"scripts": []
					},
					"configurations": {
						"production": {
							"budgets": [
								{
									"type": "initial",
									"maximumWarning": "500kb",
									"maximumError": "1mb"
								},
								{
									"type": "anyComponentStyle",
									"maximumWarning": "2kb",
									"maximumError": "4kb"
								}
							],
							"outputHashing": "all"
						},
						"development": {
							"optimization": false,
							"extractLicenses": false,
							"sourceMap": true
						}
					},
					"defaultConfiguration": "production"
				},
				"serve-original": {
					"builder": "@angular-devkit/build-angular:dev-server",
					"configurations": {
						"production": {
							"buildTarget": "template-vazio:esbuild:production"
						},
						"development": {
							"buildTarget": "template-vazio:esbuild:development"
						}
					},
					"defaultConfiguration": "development",
					"options": {
						"port": 4200
					}
				}
			}
		}
	},
	"cli": {
		"analytics": false
	}
}

package.json

{
  "name": "template-vazio",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
  },
  "private": true,
  "dependencies": {
    "@angular-architects/native-federation": "^17.1.8",
    "@angular/animations": "^17.3.0",
    "@angular/cdk": "^17.1.0",
    "@angular/common": "^17.3.0",
    "@angular/compiler": "^17.3.0",
    "@angular/core": "^17.3.0",
    "@angular/elements": "^17.3.12",
    "@angular/fire": "^17.1.0",
    "@angular/forms": "^17.3.0",
    "@angular/platform-browser": "^17.3.0",
    "@angular/platform-browser-dynamic": "^17.3.0",
    "@angular/router": "^17.3.0",
    "@nebular/date-fns": "^13.0.0",
    "@nebular/eva-icons": "file:F:/nebular/dist/eva-icons",
    "@nebular/theme": "^13.0.0",
    "@ngx-translate/core": "^15.0.0",
    "@ngx-translate/http-loader": "^8.0.0",
    "@xtr/auth": "^0.0.5-beta",
    "@xtr/ckeditor": "^2.1.19",
    "@xtr/ckeditor6": "^0.0.2",
    "@xtr/utilities": "file:D:/xtr/xtr-npm-libraries/dist/xtr/utilities",
    "@xtr/xtr-anexos": "^2.0.5",
    "date-fns": "^2.0.0",
    "es-module-shims": "^1.5.12",
    "eva-icons": "^1.0.0",
    "firebase": "^10.14.1",
    "fs": "^0.0.1-security",
    "juice": "^11.0.0",
    "ngx-toastr": "^18.0.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.14.3"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^17.3.10",
    "@angular/cli": "^17.3.10",
    "@angular/compiler-cli": "^17.3.0",
    "@schematics/angular": "^17.3.10",
    "@types/jasmine": "~5.1.0",
    "jasmine-core": "~5.1.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.2.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.1.0",
    "typescript": "~5.4.2"
  }
}

Got this error with the workaround

main.ts:5 Error: Unable to resolve specifier 'firebase/app' imported from http://localhost:4200/_angular_fire-17_1_0-dev.js
    at throwUnresolved (es-module-shims.js:477:11)
    at _resolve (es-module-shims.js:414:71)
    at es-module-shims.js:928:32
    at Array.map (<anonymous>)
    at es-module-shims.js:921:45
    at _ZoneDelegate.invoke (zone.js:369:28)
    at ZoneImpl.run (zone.js:111:43)
    at zone.js:2538:40
    at _ZoneDelegate.invokeTask (zone.js:402:33)
    at ZoneImpl.runTask (zone.js:159:47)

And this is the original error

_angular_core-17_3_12-dev.js:3806 ERROR FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - call initializeApp() first (app/no-app).
    at getApp (firebase_auth-10_14_1-dev.js:1535:25)
    at getAuth (firebase_auth-10_14_1-dev.js:9169:24)
    at _angular_fire-17_1_0-dev.js:183:44
    at _angular_fire-17_1_0-dev.js:130:57
    at _ZoneDelegate.invoke (zone.js:369:28)
    at ZoneImpl.run (zone.js:111:43)
    at _NgZone.runOutsideAngular (_angular_core-17_3_12-dev.js:8580:24)
    at runOutsideAngular (_angular_fire-17_1_0-dev.js:130:33)
    at _angular_fire-17_1_0-dev.js:183:17
    at app.config.ts:47:23

@raliand
Copy link
Author

raliand commented Nov 5, 2024

@ericaugusto-git Did you find a solution?

@ericaugusto-git
Copy link

@ericaugusto-git Did you find a solution?

sadly no, our team decided to create an angular library instead of using native federation for now, hopefully @manfredsteyer well take a look at this in the future :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants