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

Angular 9: Window is not defined #1523

Closed
MDzyga opened this issue Feb 24, 2020 · 29 comments
Closed

Angular 9: Window is not defined #1523

MDzyga opened this issue Feb 24, 2020 · 29 comments

Comments

@MDzyga
Copy link

MDzyga commented Feb 24, 2020

Description

I'm trying to migrate my solution to the latest angular universal version from AU 8 and during execute npm run dev:ssr I get an error:

...\dist-server\main.js:193133
})(window, document, 'Hammer');
   ^

ReferenceError: window is not defined

In previous version of server.ts my collegue figure it out like below:

const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync(path.join(__dirname, '.', 'dist', 'index.html')).toString();
const win = domino.createWindow(template);
const files = fs.readdirSync(`${process.cwd()}/dist-server`);

global['window'] = win;
Object.defineProperty(win.document.body.style, 'transform', {
  value: () => {
    return {
      enumerable: true,
      configurable: true,
    };
  },
});
global['document'] = win.document;

How should I set 'window' in the latest version of AU?

🔥 Exception or Error


frontend\universal\dist-server\main.js:193133
})(window, document, 'Hammer');
   ^

ReferenceError: window is not defined
    at Object../node_modules/hammerjs/hammer.js (K:\Projekty\Traceabilit frontend\universal\dist-server\main.js:193133:4)
    at __webpack_require__ (K:\Projekty\Traceabilit frontend\universal\dist-server\main.js:20:30)
    at Object../src/app/app.module.ts (K:\Projekty\Traceabilit frontend\universal\dist-server\main.js:239184:1)
    at __webpack_require__ (K:\Projekty\Traceabilit frontend\universal\dist-server\main.js:20:30)
    at Object../src/app/app.server.module.ts (K:\Projekty\Traceabilit frontend\universal\dist-server\main.js:239343:22)
    at __webpack_require__ (K:\Projekty\Traceabilit frontend\universal\dist-server\main.js:20:30)
    at Object../src/main.server.ts (K:\Projekty\Traceabilit frontend\universal\dist-server\main.js:252541:27)
    at __webpack_require__ (K:\Projekty\Traceabilit frontend\universal\dist-server\main.
js:20:30)
    at Object../server.ts (K:\Projekty\Traceabilit frontend\universal\dist-server\main.js:238941:23)
    at __webpack_require__ (K:\Projekty\Traceabilit frontend\universal\dist-server\main.js:20:30)

A server error has occurred.
node exited with 1 code.

🌍 Your Environment

"@angular/animations": "^9.0.2",
"@angular/cdk": "^9.0.1",
"@angular/common": "^9.0.2",
"@angular/compiler": "^9.0.2",
"@angular/core": "^9.0.2",
"@angular/forms": "^9.0.2",
"@angular/material": "^9.0.1",
"@angular/platform-browser": "^9.0.2",
"@angular/platform-browser-dynamic": "^9.0.2",
"@angular/pwa": "^0.900.3",
"@angular/router": "^9.0.2",
"@angular/service-worker": "^9.0.2",
"@nguniversal/common": "^9.0.1",
"@nguniversal/express-engine": "^9.0.1",

@MDzyga
Copy link
Author

MDzyga commented Feb 24, 2020

Problem was related to hammerjs, after changed it on @egjs/hammerjs solution is building

@MDzyga MDzyga closed this as completed Feb 24, 2020
@BruneXX
Copy link

BruneXX commented Mar 17, 2020

@MDzyga how is that? I'm having the same error with this component https://www.npmjs.com/package/ngx-device-detector the issue is:

/ng-app/dist/server/main.js:201150
                 this.userAgent = window.navigator.userAgent;
                                  ^
 
 ReferenceError: window is not defined
     at new DeviceDetectorService

Any ideas?

@BruneXX
Copy link

BruneXX commented Mar 17, 2020

with previous angular 8.x version, that worked perfectly.

@MDzyga
Copy link
Author

MDzyga commented Mar 17, 2020

Your problem is not related to webpack, only to define window in server.ts file.
Look at my server.ts file in my repo (https://github.com/MDzyga/universal).
You don't need to use webpack with angular universal 9. To host the latest angular universal you have to use main.js file in dist-server folder.

@BruneXX
Copy link

BruneXX commented Mar 17, 2020

Hello @MDzyga I'm not using webpack and I know that, I've already defined window in server.ts file. I don't understand why this issue arise again in angular 9, I'm using domino to get the globals, something like:

server.ts fragment:

const domino = require('domino');
const fs = require('fs');
const path = require('path');
const templateA = fs
.readFileSync(path.join('.', 'dist', 'index.html'))
.toString();
const win = domino.createWindow(templateA);
win.Object = Object;
win.Math = Math;

win.navigator.language = 'en';

global['Event'] = null;
global['window'] = win;
global['document'] = win.document;
global['branch'] = null;
global['object'] = win.object;
global['localStorage'] = localStorage;
global['sessionStorage'] = sessionstorage;
global['navigator'] = win.navigator ;

@MDzyga
Copy link
Author

MDzyga commented Mar 17, 2020

They changed a lot of things in angular.
Problem can be related to:

  1. not checking if it's browser side
  2. ivy because in angular 9 it's turn on by default

@BruneXX
Copy link

BruneXX commented Mar 17, 2020

@MDzyga

  1. The third party package is checking with isPlatformBrowser but has no validation specific for "window" (I suspect that's the issue)
  2. I've managed to disable ivy in server side, related to another issue.

@MDzyga
Copy link
Author

MDzyga commented Mar 18, 2020

@BruneXX
Do you set global variables before app function in your server.ts? Somebody from angular team told me I should change path to index.html (screenshot below).

image

I'm using too ngx-device-detector and check if you declare this module in app.module.

image

Later I don't check the platform and only use deviceDetectorService to do it, but maybe in your case is necessary to do it.

image

@BruneXX
Copy link

BruneXX commented Mar 18, 2020

Hi @MDzyga, mmm that's wierd, yes it's set before the express call const app = express();
Also I'm using the server.ts path to index I've shared above, also DeviceDetectorModule is declared in app.module.ts also I'm not checking for platformBrowser, the module it does by itself, but is not enough to check for window I can't figured out what's wrong with my configuration yet... I've no errors related to this path..
fs.readFileSync(path.join('.', 'dist', 'index.html')).toString();

@BruneXX
Copy link

BruneXX commented Mar 18, 2020

Hi @MDzyga I've performed some changes in server.ts now I'm receiving errors about not found any module, I've checked out in a started package and it seems that tsconfig.server.ts and tsconfig.app.ts haven't set the baseUrl property anymore, is that correct?

@MDzyga
Copy link
Author

MDzyga commented Mar 18, 2020

Hi, you need to set baseUrl on app folder. I noticed Angular changed in angular.json main file. Earlier I set on main.server.ts but now I had to set sever.ts.
Did you update Angular Universal like in this article? https://trilon.io/blog/angular-universal-v9-whats-new

@BruneXX
Copy link

BruneXX commented Mar 18, 2020

No, I'm not using angular-cli, do you have an example of your baseUrl? I've in my tsconfig.ts file baseUrl: './src'
tsconfig.server.ts > baseUrl: './'
tsconfig.app.ts > baseUrl: './'

@MDzyga
Copy link
Author

MDzyga commented Mar 18, 2020

tsconfig.ts -> baseUrl: 'src'
tsconfig.server.ts -> baseUrl: './'
tsconfig.app.ts -> baseUrl: './'

but server.ts was changed in the latest version of angular universal.

/***************************************************************************************************
 * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
 */
import { APP_BASE_HREF } from '@angular/common';
import { enableProdMode } from '@angular/core';
import '@angular/localize/init';
import { environment } from '@env/environment';
import { NgxRequest, NgxResponse } from '@gorniv/ngx-universal';
import { ngExpressEngine } from '@nguniversal/express-engine';
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
import * as compression from 'compression';
import * as cookieparser from 'cookie-parser';
import * as express from 'express';
import { existsSync } from 'fs';
import { join } from 'path';
import 'zone.js/dist/zone-node';

import { AppServerModule } from './src/main.server';

const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync(path.join(__dirname, '../dist', 'index.html')).toString();
const win = domino.createWindow(template);

global['window'] = win;
Object.defineProperty(win.document.body.style, 'transform', {
  value: () => {
    return {
      enumerable: true,
      configurable: true,
    };
  },
});
global['document'] = win.document;
global['CSS'] = null;
// global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
global['Prism'] = null;

enableProdMode();

// The Express app is exported so that it can be used by serverless Functions.
export function app() {
  const server = express();
  const distFolder = join(process.cwd(), 'dist');
  const indexHtml = existsSync(join(distFolder, 'index.original.html'))
    ? 'index.original.html'
    : 'index';

  // redirects!
  const redirectowww = false;
  const redirectohttps = false;
  const wwwredirecto = true;

  server.use((req: any, res: any, next: any) => {
    // for domain/index.html
    if (req.url === '/index.html') {
      res.redirect(301, 'https://' + req.hostname);
    }

    // check if it is a secure (https) request
    // if not redirect to the equivalent https url
    if (
      redirectohttps &&
      req.headers['x-forwarded-proto'] !== 'https' &&
      req.hostname !== 'localhost'
    ) {
      // special for robots.txt
      if (req.url === '/robots.txt') {
        next();

        return;
      }

      res.redirect(301, 'https://' + req.hostname + req.url);
    }

    // www or not
    if (redirectowww && !req.hostname.startsWith('www.')) {
      res.redirect(301, 'https://www.' + req.hostname + req.url);
    }

    // www or not
    if (wwwredirecto && req.hostname.startsWith('www.')) {
      const host = req.hostname.slice(4, req.hostname.length);

      res.redirect(301, 'https://' + host + req.url);
    }

    next();
  });
  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine(
    'html',
    ngExpressEngine({
      bootstrap: AppServerModule,
    }),
  );

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Example Express Rest API endpoints
  // app.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get(
    '*.*',
    express.static(distFolder, {
      maxAge: '1y',
    }),
  );

  // All regular routes use the Universal engine
  server.get('*', (req: any, res: any) => {
    global['navigator'] = req['headers']['user-agent'];
    const http = req.headers['x-forwarded-proto'] === undefined ? 'http' : req.headers['x-forwarded-proto'];

    const url = req.originalUrl;

    // tslint:disable-next-line:no-console
    console.time(`GET: ${url}`);

    res.render(indexHtml, {
      req,
      providers: [
        { provide: APP_BASE_HREF, useValue: req.baseUrl },

        // for http and cookies
        {
          provide: REQUEST,
          useValue: req,
        },
        {
          provide: RESPONSE,
          useValue: res,
        },
        /// for cookie
        {
          provide: NgxRequest,
          useValue: req,
        },
        {
          provide: NgxResponse,
          useValue: res,
        },
        // for absolute path
        {
          provide: 'ORIGIN_URL',
          useValue: `${http}://${req.headers.host}`,
        },
      ],
    },
      (err: any, html: any) => {
        if (!!err) {
          throw err;
        }

        // tslint:disable-next-line:no-console
        console.timeEnd(`GET: ${url}`);
        res.send(html);
      },
    );
  });

  return server;
}

function run() {
  const port = process.env.PORT || 80;
  // Start up the Node server
  const server = app();

  // gzip
  server.use(compression());
  // cokies
  server.use(cookieparser());

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

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = (mainModule && mainModule.filename) || '';

if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run();
}

export * from './src/main.server';

@BruneXX
Copy link

BruneXX commented Mar 18, 2020

@MDzyga thanks! I've changed to match with your server.ts and tsconfig.ts my server.ts was 90% different before migration, Now I've to made some minor changes to match yours I hope this works, otherwise IDK how to fix the lot of error TS2307: Cannot find module errors

@MDzyga
Copy link
Author

MDzyga commented Mar 18, 2020

You have to change import modules in routing like below
image

@BruneXX
Copy link

BruneXX commented Mar 18, 2020

Yes, I've already done that in v8.x

@BruneXX
Copy link

BruneXX commented Mar 18, 2020

Hi @MDzyga after a lot of changes in tsconfig files I've managed to reach to a new error xD
and I found an issue related: webpack/webpack#5939

ReferenceError: __non_webpack_require__ is not defined

Have you experienced something similar?

Thanks!

@BruneXX
Copy link

BruneXX commented Mar 18, 2020

I'd to validate that __non_webpack_require__ and also change the "declare const" just by "let" ohterwise the error always arise..

@MDzyga
Copy link
Author

MDzyga commented Mar 18, 2020

I didn't have this problem

@BruneXX
Copy link

BruneXX commented Mar 18, 2020

now I've the issue that is that I cannot serve (with node) angular universal... the commnad node dist/server is doing nothing...

@BruneXX
Copy link

BruneXX commented Mar 18, 2020

Also I've tried specifically with full path to file... node dist/server/main.js but nothing happens..

@MDzyga
Copy link
Author

MDzyga commented Mar 18, 2020

I'm using angular cli so maybe this package changes something

@BruneXX
Copy link

BruneXX commented Mar 18, 2020

Can you share how you serve the universal app after build?

@BruneXX
Copy link

BruneXX commented Mar 18, 2020

Regarding this: https://trilon.io/blog/angular-universal-v9-whats-new is still the same... just node dist/server/main.js but doesn't work in my case.

@MDzyga
Copy link
Author

MDzyga commented Mar 19, 2020

Check angular.json settings.

"build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist",
            "index": "src/index.html",
            "main": "src/main.browser.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "assets": [
              "src/assets",
              "src/favicon.png",
              "src/robots.txt",
              "src/manifest.json"
            ],
            "styles": [],
            "scripts": []
          },
          "configurations": {
            "local": {
              "optimization": true,
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "aot": true,
              "serviceWorker": true,
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/browser/environment.ts"
                }
              ]
            },
"server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist-server",
            "main": "server.ts",
            "tsConfig": "src/tsconfig.server.json"
          },
          "configurations": {
            "local": {
              "optimization": true,
              "sourceMap": false,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": true,
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/server/environment.ts"
                }
              ]
            },

I serve my app executing ssr:watch script

"start": "node --inspect ./dist-server/main.js",
"ssr:watch": "SET PORT=90 && run-p ssr:universal:build:*",
"ssr:universal:build:browser": "ng run traceabilit-universal:build --watch",
"ssr:universal:build:server": "node ./node_modules/npm-delay 15000 && ng run traceabilit-universal:server --watch",
"ssr:universal:build:nodemon": "node ./node_modules/npm-delay 30000 && nodemon --inspect ./dist-server/main.js",

@yazan-mehrez
Copy link

@MDzyga I checked your repo, where did you define the webpack.config.js in your code? It seems like you are not using it. I'm facing a problem that is related to webpack loaders, I can define ts-loader in webpack.config.js but how can I define it in server.ts or cli? I'm wondering how you built the project using webpack.config.js without using custom builders in cli.

@MDzyga
Copy link
Author

MDzyga commented Mar 26, 2020

I'm using Angular cli and currently it doesn't need custom webpack to build an application

@yazan-mehrez
Copy link

But how without using any special loaders? I'm getting this error "Module parse failed: Unexpected character '�' (1:2)"

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Apr 26, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants