-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
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
- running
npm install
- running
npm run build
- 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 **
Expected behavior
A running local server.
Actual behavior
Server not running due to missing main.