skip to Main Content

I am currently working on Angular using its 3rd party library such as Angular CLI/Angular Universal following this link https://github.com/angular/angular-cli/wiki/stories-universal-rendering / and firebase hosting / and real-time database. And it working great on my local machine listening in, localhost:4000. I see it render server-side rendering smoothly and I see the HTML which I need for SEO friendly I use also lazy loading which improves the loading of web application performance very fast. Now, my problem is, after I deploy my Angular application with Universal Server-side Rendering also with lazy loading. I get this error saying, Error: could not handle the request and also if I just deploy without lazy loading I get this 3 errors,

1. Uncaught SyntaxError: Unexpected token < inline.318b50c57b4eba3d437b.bundle.js:1 

2. Uncaught SyntaxError: Unexpected token < polyfills.bf95165a1d5098766b92.bundle.js:1

3. Uncaught SyntaxError: Unexpected token < main.4871dfc6be55ccbf4c0b.bundle.js:1 

HERE’S THE FOLDER STRUCTURE:

  • dist => this is the build folder of the angular application

    • browser
    • server
    • server.js
  • e2e

  • functions => this is the build folder and I use to upload into firebase hosting
    • browser
    • node_modules
    • server
    • .eslintrc.json
    • index.js
    • package-lock.json
    • package.json
    • server.js
  • node_modules
  • server => I use to transpile index.ts to index.js
    • index.ts
    • tsconfig.functions.json
  • src => main directory for the angular application
    • app
      • demo
        • buttons
          • buttons.component.css
          • buttons.component.html
          • buttons.component.spec.ts
          • buttons.component.ts
        • demo-routing.module.ts
        • demo.module.ts
      • app.component.css
      • app.component.html
      • app.component.spec.ts
      • app.component.ts
      • app.module.ts
      • app.server.module.ts
    • assets
    • environments
    • favicon.ico
    • index.html
    • main.server.ts
    • main.ts
    • polyfills.ts
    • styles.css
    • test.ts
    • tsconfig.app.json
    • tsconfig.server.json
    • typings.d.ts
  • .angular-cli.json
  • .editorconfig
  • .firebaserc
  • .gitignore
  • firebase.json
  • karma.conf.js
  • package-lock.json
  • package.json
  • protractor.conf.js
  • README.md
  • server.ts
  • tsconfig.json
  • tslint.json
  • webpack.server.config.js

FOR, src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { Routes, RouterModule } from '@angular/router';

import { AppComponent } from './app.component';

import { ButtonsComponent } from './demo/buttons/buttons.component';

const routes: Routes = [
  { path: 'buttons', loadChildren: './demo/demo.module#DemoModule' },
  { path: '**', redirectTo: 'buttons' }
];

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'something-unique-id' }),
    RouterModule.forRoot(routes)
  ],
  providers: [],
  bootstrap: [
    AppComponent,
    ButtonsComponent
  ]
})

export class AppModule { }

FOR, src/app/app.server.module.ts

 import {NgModule} from '@angular/core';
    import {ServerModule} from '@angular/platform-server';
    import {ModuleMapLoaderModule} from '@nguniversal/module-map-ngfactory-loader';

    import {AppModule} from './app.module';
    import {AppComponent} from './app.component';

    @NgModule({
      imports: [
        // The AppServerModule should import your AppModule followed
        // by the ServerModule from @angular/platform-server.
        AppModule,
        ServerModule, 
        ModuleMapLoaderModule // <-- *Important* to have lazy-loaded routes work
      ],
      // Since the bootstrapped component is not inherited from your
      // imported AppModule, it needs to be repeated here.
      bootstrap: [AppComponent],
    })
    export class AppServerModule {}

FOR, src/main.server.ts

export { AppServerModule } from './app/app.server.module';

FOR, src/tsconfig.server.json

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "baseUrl": "./",
    // Set the module format to "commonjs":
    "module": "commonjs",
    "types": []
  },
  "exclude": [
    "test.ts",
    "**/*.spec.ts"
  ],
  // Add "angularCompilerOptions" with the AppServerModule you wrote
  // set as the "entryModule".
  "angularCompilerOptions": {
    "entryModule": "app/app.server.module#AppServerModule"
  }
}

FOR, .angular-cli.json

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "project": {
    "name": "kuntento"
  },
  "apps": [
    {
      "root": "src",
      "outDir": "dist/browser",
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.app.json",
      "testTsconfig": "tsconfig.spec.json",
      "prefix": "app",
      "styles": [
        "styles.css"
      ],
      "scripts": [],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    },
    {
      "platform": "server",
      "root": "src",
      "outDir": "dist/server",
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "index": "index.html",
      "main": "main.server.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.server.json",
      "testTsconfig": "tsconfig.spec.json",
      "prefix": "app",
      "styles": [
        "styles.css"
      ],
      "scripts": [],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    }
  ],
  "e2e": {
    "protractor": {
      "config": "./protractor.conf.js"
    }
  },
  "lint": [
    {
      "project": "src/tsconfig.app.json",
      "exclude": "**/node_modules/**"
    },
    {
      "project": "src/tsconfig.spec.json",
      "exclude": "**/node_modules/**"
    },
    {
      "project": "e2e/tsconfig.e2e.json",
      "exclude": "**/node_modules/**"
    }
  ],
  "test": {
    "karma": {
      "config": "./karma.conf.js"
    }
  },
  "defaults": {
    "styleExt": "css",
    "component": {}
  }
}

FOR, ./server.ts

    import 'zone.js/dist/zone-node';
    import 'reflect-metadata';

    import { renderModuleFactory } from '@angular/platform-server';
    import { enableProdMode } from '@angular/core';

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

    enableProdMode();

    const app = express();

    const PORT = process.env.PORT || 4000;
    const DIST_FOLDER = join(process.cwd(), 'dist');

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

    const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.bundle');

    const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader');

    app.engine('html', (_, options, callback) => {
        renderModuleFactory(AppServerModuleNgFactory, {
            document: template,
            url: options.req.url,
            extraProviders: [
                provideModuleMap(LAZY_MODULE_MAP)
            ]
        }).then(html => {
            callback(null, html);
        });
    });

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

    app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));

    app.get('*', (req, res) => {
        res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req });
    });

    app.listen(PORT, () => {
        console.log(`Node server listening on http://localhost:${PORT}`);
    });

FOR, ./webpack.server.config.js

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

module.exports = {
    entry: { server: './server.ts' },
    resolve: { extensions: ['.js', '.ts'] },
    target: 'node',
    externals: [/(node_modules|main..*.js)/],
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js'
    },
    module: {
        rules: [
            { test: /.ts$/, loader: 'ts-loader' }
        ]
    },
    plugins: [
        new webpack.ContextReplacementPlugin(
            /(.+)?angular(\|/)core(.+)?/,
            path.join(__dirname, 'src'),
            {}
        ),
        new webpack.ContextReplacementPlugin(
            /(.+)?express(\|/)(.+)?/,
            path.join(__dirname, 'src'),
            {}
        )
    ]
}

FOR, package.json

{
  "name": "universalrendering",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build --prod",
    "build:ssr": "npm run build:client-and-server-bundles && npm run webpack:server",
    "serve:ssr": "node dist/server.js",
    "build:client-and-server-bundles": "ng build --prod && ng build --prod --app 1 --output-hashing=false",
    "webpack:server": "webpack --config webpack.server.config.js --progress --colors",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^5.2.0",
    "@angular/common": "^5.2.0",
    "@angular/compiler": "^5.2.0",
    "@angular/core": "^5.2.0",
    "@angular/forms": "^5.2.0",
    "@angular/http": "^5.2.0",
    "@angular/platform-browser": "^5.2.0",
    "@angular/platform-browser-dynamic": "^5.2.0",
    "@angular/platform-server": "^5.2.6",
    "@angular/router": "^5.2.0",
    "@nguniversal/module-map-ngfactory-loader": "^5.0.0-beta.5",
    "angular-universal-express-firebase": "0.0.4",
    "core-js": "^2.4.1",
    "express": "^4.16.2",
    "firebase-functions": "^0.8.1",
    "rxjs": "^5.5.6",
    "ts-loader": "^3.5.0",
    "zone.js": "^0.8.19"
  },
  "devDependencies": {
    "@angular/cli": "^1.7.1",
    "@angular/compiler-cli": "^5.2.0",
    "@angular/language-service": "^5.2.0",
    "@types/jasmine": "~2.8.3",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "^4.0.1",
    "jasmine-core": "~2.8.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~2.0.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-node": "~4.1.0",
    "tslint": "~5.9.1",
    "typescript": "^2.7.2"
  }
}

THIS IS FOR Firebase Hosting:

  • server forlder

FOR, index.html

import * as functions from 'firebase-functions';
import * as angularUniversal from 'angular-universal-express-firebase';

export let server_rendering = angularUniversal.trigger({
    index: __dirname + '/browser/index.html',
    main: __dirname + '/server/main.bundle',
    enableProdMode: true,
    browserCacheExpiry: 1200,
    cdnCacheExpiry: 600
});

FOR, tsconfig.functions.json

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es2015",
        "rootDir": ".",
        "outDir": "../functions"
    },
    "files": [
        "index.ts"
    ]
}
  • functions folder

  • functions => this is the build folder and I use to upload into firebase hosting

    • browser
    • node_modules
    • server
    • .eslintrc.json
    • index.js
    • package-lock.json
    • package.json
    • server.js

FOR, package.json

{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "lint": "./node_modules/.bin/eslint .",
    "serve": "firebase serve --only functions",
    "shell": "firebase experimental:functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "dependencies": {
    "firebase-admin": "~5.8.1",
    "firebase-functions": "^0.8.1",
    "@angular/animations": "^5.2.0",
    "@angular/common": "^5.2.0",
    "@angular/compiler": "^5.2.0",
    "@angular/core": "^5.2.0",
    "@angular/forms": "^5.2.0",
    "@angular/http": "^5.2.0",
    "@angular/platform-browser": "^5.2.0",
    "@angular/platform-browser-dynamic": "^5.2.0",
    "@angular/platform-server": "^5.2.6",
    "@angular/router": "^5.2.0",
    "@nguniversal/module-map-ngfactory-loader": "^5.0.0-beta.5",
    "angular-universal-express-firebase": "0.0.4",
    "core-js": "^2.4.1",
    "express": "^4.16.2",
    "rxjs": "^5.5.6",
    "ts-loader": "^3.5.0",
    "zone.js": "^0.8.19"
  },
  "devDependencies": {
    "eslint": "^4.12.0",
    "eslint-plugin-promise": "^3.6.0"
  },
  "private": true
}

FOR, index.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const angularUniversal = require("angular-universal-express-firebase");
exports.server_rendering = angularUniversal.trigger({
    index: __dirname + '/browser/index.html',
    main: __dirname + '/server/main.bundle',
    enableProdMode: true,
    browserCacheExpiry: 1200,
    cdnCacheExpiry: 600
});

3

Answers


  1. Please check the same issue here, usually that error when your redirect rules are sending the index page instead of the javascript.

    Update your package.json scripts as like follows:

            "scripts": {
              "ng": "ng",
              "start": "ng serve",
              "build": "ng build --prod",
              "build.server": "ng build -aot -app ssr",
              "test": "ng test",
              "lint": "ng lint",
              "e2e": "ng e2e"
            },
    

    And then .angular-cli.json "main": "main.ts" to "main": "main.server.ts".

    Build browser and server versions of the app

    npm run build
    npm run build.server
    

    Please check the tutorial to here Deploy Angular Universal w/ Firebase and demo.

    Hopes this will help you !!

    Login or Signup to reply.
  2. My problem was in the supervisor.conf file. The line with the environment in node was misspelling. The correct line is:

    environment=NODE_ENV=production

    This causes all my ‘unexpected token <‘ problems

    Login or Signup to reply.
  3. I had a similar issue to you and was able to resolve it by moving my server.js file from the DIST folder into the directory above.

    So my folder/file structure looks like this:

    • webroot – root folder

      • server.js

      • dist (folder)

        • browser (subfolder)

        • server (subfolder)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search