Skip to content

Angular 11.0.7 Universal SSR with Firebase + PWA not working #2737

@muellerdberlin

Description

@muellerdberlin

Since days i'm trying to build a running Angular (latest version) with Server-Side-Rendering using ANgular Universal and Firebase Cloud Function. I was following different guides in the internet and ended up at https://github.com/angular/angularfire/blob/master/docs/universal/getting-started.md. Unfortunately it still doesn't work on localhost nor as cloud function. I hope this is the correct way to request an answer and someone can help me. Thank you a lot!

Version info

Angular 11.0.7

"@angular/fire": "^6.1.4",
"@nguniversal/builders": "^11.0.1",
"@nguniversal/common": "^11.0.1",
"@nguniversal/express-engine": "^11.0.1",
"@nguniversal/module-map-ngfactory-loader": "^8.2.6",
"express": "^4.17.1",
"firebase": "^7.0 || ^8.0",
"firebase-admin": "^8.10.0",
"firebase-functions": "^3.6.0",
"firebase-functions-test": "^0.2.2",
"firebase-tools": "^8.0.0",
"typescript": "~4.0.2",
"typescript-map": "^0.1.0",
"webpack-cli": "^4.3.1",
"ws": "^7.4.2",
"xhr2": "^0.2.0"

How to reproduce these conditions

related to https://github.com/angular/angularfire/blob/master/docs/universal/getting-started.md
start npm run build && npm run serve:ssr

package.json commands

"build": "ng build && npm run build:ssr",
"build:ssr": "ng run project:server && npm run webpack:ssr",
"webpack:ssr": "webpack --config webpack.server.config.js --progress --color",
"serve:ssr": "node dist/project/webpack/server.js"

Steps to set up and reproduce

  1. running npm install
  2. running npm run build
  3. running npm run serve:ssr

Sample data and security rules

angular.json configuration

"build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/project/browser",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "aot": true,
            "assets": [
              "src/favicon.ico",
              "src/assets",
              "src/manifest.webmanifest"
            ],
            "styles": [
              "src/assets/styles/styles.scss"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "500kb",
                  "maximumError": "9mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "2kb",
                  "maximumError": "10kb"
                }
              ],
              "serviceWorker": true,
              "ngswConfigPath": "ngsw-config.json"
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "project:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "project:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "project:build"
          }
        },
        "server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist/project/server",
            "main": "server.ts",
            "tsConfig": "tsconfig.server.json",
            "externalDependencies": [
              "firebase",
              "@firebase/app",
              "@firebase/analytics",
              "@firebase/auth",
              "@firebase/component",
              "@firebase/database",
              "@firebase/firestore",
              "@firebase/functions",
              "@firebase/installations",
              "@firebase/messaging",
              "@firebase/storage",
              "@firebase/performance",
              "@firebase/remote-config",
              "@firebase/util"
            ]
          },
          "configurations": {
            "production": {
              "outputHashing": "media",
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "sourceMap": false,
              "optimization": true
            }
          }
        },
        "serve-ssr": {
          "builder": "@nguniversal/builders:ssr-dev-server",
          "options": {
            "browserTarget": "project:build",
            "serverTarget": "project:server"
          },
          "configurations": {
            "production": {
              "browserTarget": "project:build:production",
              "serverTarget": "project:server:production"
            }
          }
        },
        "prerender": {
          "builder": "@nguniversal/builders:prerender",
          "options": {
            "browserTarget": "project:build:production",
            "serverTarget": "project:server:production",
            "routes": [
              "/"
            ]
          },
          "configurations": {
            "production": {}
          }
        },
        "deploy": {
          "builder": "@angular/fire:deploy",
          "options": {
            "ssr": true
          }
        }

webpack.server.config.js

const path = require('path');
const webpack = require('webpack');

const APP_NAME = 'project'; // TODO: replace me!

module.exports = {
  entry: {server: './server.ts'},
  resolve: {extensions: ['.js', '.ts']},
  mode: 'development',
  target: 'node',
  externals: [
    /* Firebase has some troubles being webpacked when in
       in the Node environment, let's skip it.
       Note: you may need to exclude other dependencies depending
       on your project. */
    /^firebase/
  ],
  output: {
    // Export a UMD of the webpacked server.ts & deps, for
    // rendering in Cloud Functions
    path: path.join(__dirname, `dist/${APP_NAME}/webpack`),
    library: 'app',
    libraryTarget: 'umd',
    filename: '[name].js'
  },
  module: {
    rules: [
      {test: /\.ts$/, loader: 'ts-loader'}
    ]
  },
  plugins: [
    new webpack.ContextReplacementPlugin(
      /(.+)?angular(\\|\/)core(.+)?/,
      path.join(__dirname, 'src'), // location of your src
      {} // a map of your routes
    ),
    new webpack.ContextReplacementPlugin(
      /(.+)?express(\\|\/)(.+)?/,
      path.join(__dirname, 'src'),
      {}
    )
  ]
}

server.ts

// These are important and needed before anything else
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import 'globalthis/auto'; // got imported in polyfill.ts

import {enableProdMode} from '@angular/core';
import {ngExpressEngine} from '@nguniversal/express-engine';
import {provideModuleMap} from '@nguniversal/module-map-ngfactory-loader';

import * as express from 'express';
import {join} from 'path';
import {readFileSync} from 'fs';

// Polyfills required for Firebase
(global as any).WebSocket = require('ws');
(global as any).XMLHttpRequest = require('xhr2');

// Faster renders in prod mode
// enableProdMode();

// Export our express server
export const app = express();

const DIST_FOLDER = join(process.cwd(), 'dist');
const APP_NAME = 'project'; // TODO: replace me!

const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require(`./dist/${APP_NAME}/server/main`);

// index.html template
const template = readFileSync(join(DIST_FOLDER, APP_NAME, 'index.html')).toString();

app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));

app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, APP_NAME));

// Serve static files
app.get('*.*', express.static(join(DIST_FOLDER, APP_NAME)));

// All regular routes use the Universal engine
app.get('*', (req, res) => {
  res.render(join(DIST_FOLDER, APP_NAME, 'index.html'), {req});
});

// If we're not in the Cloud Functions environment, spin up a Node server
if (!process.env.FUNCTION_NAME) {
  const PORT = process.env.PORT || 4000;
  app.listen(PORT, () => {
    console.log(`Node server listening on http://localhost:${PORT}`);
  });
}

Debug output

Building /browser

✔ Browser application bundle generation complete.
✔ Copying assets complete.
✔ Index html generation complete.

Building /server

✔ Server application bundle generation complete.

Initial Chunk Files | Names         |    Size
main.js             | main          | 5.68 MB

                    | Initial Total | 5.68 MB

Building /webpack

> webpack --config webpack.server.config.js --progress --color

Hash: 5ecb502bc2d052f5c3f1
Version: webpack 4.46.0
Time: 17714ms
Built at: 01/15/2021 2:38:29 PM
    Asset      Size  Chunks             Chunk Names
server.js  6.03 MiB  server  [emitted]  server
Entrypoint server = server.js
[./dist sync recursive ^\.\/.*\/server\/main$] ./dist sync ^\.\/.*\/server\/main$ 186 bytes {server} [built]
[./dist/project/server/main.js] 5.78 MiB {server} [optional] [built]
[./server.ts] 1.59 KiB {server} [built]
[@nguniversal/express-engine] external "@nguniversal/express-engine" 42 bytes {server} [built]
[@nguniversal/module-map-ngfactory-loader] external "@nguniversal/module-map-ngfactory-loader" 42 bytes {server} [built]
[buffer] external "buffer" 42 bytes {server} [built]
[crypto] external "crypto" 42 bytes {server} [built]
[express] external "express" 42 bytes {server} [built]
[fs] external "fs" 42 bytes {server} [built]
[globalthis/auto] external "globalthis/auto" 42 bytes {server} [built]
[path] external "path" 42 bytes {server} [built]
[reflect-metadata] external "reflect-metadata" 42 bytes {server} [built]
[ws] external "ws" 42 bytes {server} [built]
[xhr2] external "xhr2" 42 bytes {server} [built]
[zone.js/dist/zone-node] external "zone.js/dist/zone-node" 42 bytes {server} [built]
    + 14 hidden modules

** Errors in the JavaScript console **

> node dist/project/webpack/server.js

webpack://app/./dist/project/server/main.js?:42279
        throw e;
        ^

Error: Cannot find module './project/server/main'
    at webpackEmptyContext (webpack://app/./dist/project/server/main.js?:42277:10)
    at Module.uj+Y (webpack://app/./dist/project/server/main.js?:134772:82)
    at __webpack_require__ (webpack://app/./dist/project/server/main.js?:20:30)
    at Object.0 (webpack://app/./dist/project/server/main.js?:511:18)
    at __webpack_require__ (webpack://app/./dist/project/server/main.js?:20:30)
    at +fbb.module.exports (webpack://app/./dist/project/server/main.js?:84:18)
    at eval (webpack://app/./dist/project/server/main.js?:87:10)
    at Object../dist/project/server/main.js (/Users/path/dist/project/webpack/server.js:117:1)
    at __webpack_require__ (/Users/path/dist/project/webpack/server.js:30:30)
    at webpackContext (webpack://app/./dist_sync_^\.\/.*\/server\/main$?:8:9)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! project@0.0.0 serve:ssr: `node dist/project/webpack/server.js`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the project@0.0.0 serve:ssr script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/path/.npm/_logs/2021-01-15T21_50_25_424Z-debug.log

** Screenshots **

grafik

Expected behavior

A running local server.

Actual behavior

Server not running due to missing main.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions