lucaswxp
lucaswxp

Reputation: 2119

Angular universal/Server side rendering missing files

I'm trying to make my angular app to ssr, in the official documentation it says that I should run

webpack --config webpack.server.config.js --progress --colors

But I can't get it to compile correctly:

ERROR in ./server.ts
Module not found: Error: Can't resolve './server/main' in '/home/lucaswxp/reduza/reduza-angular'
 @ ./server.ts 18:9-33

Line with problem:

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

These files are supposed to be created dynamically, that's why they are required with "require", but there's no "server/main.js" file in the dist folder anywhere, this is what I got in the dist folder:

out-tsc/
   e2e/
   src/
   node_modules/
reduza-ui/ <-- my project name
   main.08ht283hg20s3.js
server.js
server.js.map

And my angular.json config:

"server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist/reduza-ui",
            "main": "src/server.main.ts",
            "tsConfig": "src/server.tsconfig.app.json"
          },
          "configurations": {
            "stable": {
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "fileReplacements": [
                { 
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ]
            }
          }
        }

There's no "server" folder. I can't figure what did I miss? I tried including manually the file dist/reduza-ui/main.08ht283hg20s3.js, this compiled, but I got the following error acessing localhost:4000:

TypeError: Cannot read property 'moduleType' of undefined
    at /home/lucaswxp/reduza/reduza-angular/dist/server.js:151:1457
    at e.invoke (/home/lucaswxp/reduza/reduza-angular/dist/server.js:1809:7001)
    at Object.onInvoke (/home/lucaswxp/reduza/reduza-angular/dist/server.js:137:1986)
    at e.invoke (/home/lucaswxp/reduza/reduza-angular/dist/server.js:1809:6941)
    at t.run (/home/lucaswxp/reduza/reduza-angular/dist/server.js:1809:2171)
    at e.run (/home/lucaswxp/reduza/reduza-angular/dist/server.js:137:2710)
    at e.bootstrapModuleFactory (/home/lucaswxp/reduza/reduza-angular/dist/server.js:151:1388)
    at md (/home/lucaswxp/reduza/reduza-angular/dist/server.js:1673:888)
    at p.engine (/home/lucaswxp/reduza/reduza-angular/dist/server.js:1794:10074)
    at p.render (/home/lucaswxp/reduza/reduza-angular/dist/server.js:1984:1029)

Upvotes: 1

Views: 3144

Answers (1)

Marc J. Schmidt
Marc J. Schmidt

Reputation: 8469

This is an error in the documentation.

Their documentation's code is

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

However, this not correct, or at least not very clear.

Point 1: You should not build in production mode.

Although you see at one line in the documentation that they want you to include in the scripts section of your package.json a line like

ng run my-project:server:production

You shouldn't do that, for two reasons: First, their instruction to change the angular.json does not include the production build. Second, the production build (if you just copied from the production of your actual non-server app) is made in a way to include a hash in the filename, which leads to a file named like main.a220df2.js. This of course is not ideal, as this would mean you have to change your server.ts all the time.

So, either disable hashing "outputHashing": "none" or simply don't do a production build by calling ng run my-project:server (simply remove :production).

Point 2: Use correct require

The actual require() call should point to your built main.server.ts, which is references in your angular.json under the section projects:PROJECTNAME:architect:server. The documentation has defined the outputPath as "outputPath": "dist/my-project-server",, which means we have a file in dist/my-project-server/main.js laying around (take care of point 1 first and disable outputHashing or don't build in production mode at all). Exactly this file needs to be required in your server.ts.

So, in your case, it's the file ./dist/reduza-ui/main.js you should include, once your have disabled outputHasing or stopped building in production mode.

const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/reduza-ui/main');

An additional tip: At the very top of the documentation, you see a link to the complete code of the whole article: https://angular.io/generated/zips/universal/universal.zip. There you can see what the real code should look like.

Two last tip to get it fully working:

First

The documentation says in server.ts something like:

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

The browser is the actual default version of your app, so your regular build. If you do ng build you end up having a folder in dist/ that is your actual default app. This app is meant by browser. In your case you named your server version reduza-ui, so maybe your actual app is named reduza-ui-default in dist. In server.ts needs to be 'reduza-ui-default' instead of 'browser' on several places.

Second

At this point, when you compiled your server, started it, and requested your first page in your browser, you get in your console:

StaticInjectorError(Platform: core)[InjectionToken Application Initializer -> InjectionToken DocumentToken]:
  Right-hand side of 'instanceof' is not an object

This is because webpack minimizes your server.ts. Disable that using

optimization: {
    minimize: false
},

in webpack.server.config.js.

Upvotes: 2

Related Questions